package cn.com.cifi.mars.service.impl;
import static java.util.Arrays.asList;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.sql.DataSource;

import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import cn.com.cifi.mars.bean.bo.PortalTodoBo;
import cn.com.cifi.mars.bean.bo.approval.ApprovalSubmitBO;
import cn.com.cifi.mars.bean.bo.projecyByStages.FullPathOrgData;
import cn.com.cifi.mars.bean.bo.returnPlan.CombinationProductDataBO;
import cn.com.cifi.mars.bean.bo.returnPlan.ReturnApprovalBO;
import cn.com.cifi.mars.bean.dto.ProjectByStagesDto;
import cn.com.cifi.mars.bean.dto.returnPlan.CombinationProductDataDto;
import cn.com.cifi.mars.bean.dto.returnPlan.ReturnCoefficientDetailDto;
import cn.com.cifi.mars.bean.param.returnPlan.ReturnPlanApprovalSubmitParam;
import cn.com.cifi.mars.bean.param.returnPlan.ReturnPlanCreateParam;
import cn.com.cifi.mars.bean.param.returnPlan.ReturnPlanDetailSaveParam;
import cn.com.cifi.mars.bean.vo.price.FilePreviewVO;
import cn.com.cifi.mars.bean.vo.returnPlan.ReturnCreateSourceDataVo;
import cn.com.cifi.mars.bean.vo.returnPlan.ReturnPlanApprovalPreviewVo;
import cn.com.cifi.mars.bean.vo.returnPlan.ReturnPlanCoeffcienDifferenceDetailVo;
import cn.com.cifi.mars.bean.vo.returnPlan.ReturnPlanCoeffcienDifferenceVo;
import cn.com.cifi.mars.bean.vo.returnPlan.ReturnPlanDetailDataVo;
import cn.com.cifi.mars.bean.vo.returnPlan.ReturnPlanDetailMonthData;
import cn.com.cifi.mars.bean.vo.returnPlan.ReturnPlanDetailYearData;
import cn.com.cifi.mars.bean.vo.returnPlan.ReturnPlanEditDataVo;
import cn.com.cifi.mars.bean.vo.returnPlan.ReturnPlanGroupVo;
import cn.com.cifi.mars.bean.vo.returnPlan.ReturnPlanOverviewDataVo;
import cn.com.cifi.mars.bean.vo.returnPlan.ReturnPlanProjectOverallYearTargetVo;
import cn.com.cifi.mars.bean.vo.returnPlan.ReturnPlanProjectYearTargetVo;
import cn.com.cifi.mars.bean.vo.returnPlan.ReturnPlanRegionYearTargetVo;
import cn.com.cifi.mars.bean.vo.returnPlan.ReturnPlanReturnCoefficientDetailVo;
import cn.com.cifi.mars.bean.vo.returnPlan.ReturnPlanStageCoeffcienDifferenceVo;
import cn.com.cifi.mars.bean.vo.returnPlan.ReturnPlanVersionDataVo;
import cn.com.cifi.mars.bean.vo.returnPlan.ReturnPlanYearDataVo;
import cn.com.cifi.mars.bean.vo.returnPlan.ReturnPlanYearTargetDetailVo;
import cn.com.cifi.mars.bean.vo.returnPlan.ReturnPlantMonthTargetDetailVo;
import cn.com.cifi.mars.bean.vo.returnPlan.SignAndReturnVo;
import cn.com.cifi.mars.common.exception.MsgException;
import cn.com.cifi.mars.constant.ApprovedStateNameCodeEnum;
import cn.com.cifi.mars.constant.BigStandardFormatsEnum;
import cn.com.cifi.mars.constant.CodeEnum;
import cn.com.cifi.mars.constant.Constants;
import cn.com.cifi.mars.constant.FetchNnumberEnum;
import cn.com.cifi.mars.constant.IsDeleteEnum;
import cn.com.cifi.mars.constant.LogEventEnum;
import cn.com.cifi.mars.constant.ModuleTypeEunm;
import cn.com.cifi.mars.constant.OperatorTypeEnum;
import cn.com.cifi.mars.constant.OrgTypeEunm;
import cn.com.cifi.mars.constant.ProcessTypeEnum;
import cn.com.cifi.mars.constant.ReturnVersionTypeEnum;
import cn.com.cifi.mars.constant.SalyTypeEnum;
import cn.com.cifi.mars.constant.StageStatusEnum;
import cn.com.cifi.mars.constant.StandardFormatsMapEnum;
import cn.com.cifi.mars.constant.TodoOrgTypeEnum;
import cn.com.cifi.mars.constant.TodoTypeCodeEnum;
import cn.com.cifi.mars.constant.VersionCreateEnum;
import cn.com.cifi.mars.constant.VersionEventEnum;
import cn.com.cifi.mars.constant.VersionIdTypeEnum;
import cn.com.cifi.mars.constant.VersionStatusEnum;
import cn.com.cifi.mars.constant.VersionTypeEunm;
import cn.com.cifi.mars.constant.WhetherEnum;
import cn.com.cifi.mars.entity.ApprovalBill;
import cn.com.cifi.mars.entity.ApprovalBillDetail;
import cn.com.cifi.mars.entity.MdmProject;
import cn.com.cifi.mars.entity.MdmStage;
import cn.com.cifi.mars.entity.MymRooms;
import cn.com.cifi.mars.entity.PortalTodo;
import cn.com.cifi.mars.entity.PortalTodoTask;
import cn.com.cifi.mars.entity.ReturnCoefficientDetail;
import cn.com.cifi.mars.entity.ReturnPlanVersion;
import cn.com.cifi.mars.entity.ReturnPlanVersionDetail;
import cn.com.cifi.mars.entity.SignPlanVersion;
import cn.com.cifi.mars.entity.VersionLog;
import cn.com.cifi.mars.entity.common.IdmUser;
import cn.com.cifi.mars.entity.common.SysProjectFetchNumber;
import cn.com.cifi.mars.mapper.mars.DmCityReturnCoefficientMapper;
import cn.com.cifi.mars.mapper.mars.PortalTodoMapper;
import cn.com.cifi.mars.mapper.mars.PortalTodoTaskMapper;
import cn.com.cifi.mars.mapper.mars.ProjectByStagesMapper;
import cn.com.cifi.mars.mapper.mars.ProjectMapper;
import cn.com.cifi.mars.mapper.mars.ProjectReturnCoefficientMapper;
import cn.com.cifi.mars.mapper.mars.ReturnPlanVersionCoefficientMapper;
import cn.com.cifi.mars.mapper.mars.ReturnPlanVersionDetailMapper;
import cn.com.cifi.mars.mapper.mars.ReturnPlanVersionMapper;
import cn.com.cifi.mars.mapper.mars.SignPlanVersionMapper;
import cn.com.cifi.mars.mapper.mars.SignPlanVersionSupplyDetailMapper;
import cn.com.cifi.mars.mapper.mars.StageMapper;
import cn.com.cifi.mars.mapper.mars.SupplyPlanVersionDetailMapper;
import cn.com.cifi.mars.mapper.wrapper.PriceVersionWrapper;
import cn.com.cifi.mars.mapper.wrapper.ReturnPlanVersionDetailWrapper;
import cn.com.cifi.mars.mapper.wrapper.ReturnPlanVersionWrapper;
import cn.com.cifi.mars.mapper.wrapper.SignPlanVersionWrapper;
import cn.com.cifi.mars.service.ApprovalBillService;
import cn.com.cifi.mars.service.ApprovalPreviewService;
import cn.com.cifi.mars.service.BudgetLockRecordService;
import cn.com.cifi.mars.service.BudgetVersionIdentifyService;
import cn.com.cifi.mars.service.CommonService;
import cn.com.cifi.mars.service.FileUploadService;
import cn.com.cifi.mars.service.IdmUserService;
import cn.com.cifi.mars.service.MymRoomsService;
import cn.com.cifi.mars.service.ProjectBasicService;
import cn.com.cifi.mars.service.ProjectByStagesService;
import cn.com.cifi.mars.service.ReturnPlanVersionCoefficientService;
import cn.com.cifi.mars.service.ReturnPlanVersionDetailService;
import cn.com.cifi.mars.service.ReturnPlanVersionService;
import cn.com.cifi.mars.service.SignPlanVersionDetailService;
import cn.com.cifi.mars.service.SignPlanVersionService;
import cn.com.cifi.mars.service.SubmitApprovedProjectService;
import cn.com.cifi.mars.service.SysProjectFetchNumberService;
import cn.com.cifi.mars.service.VersionLogService;
import cn.com.cifi.mars.util.BigDecimalUtils;
import cn.com.cifi.mars.util.DateUtils;
import cn.com.cifi.mars.util.ListUtils;
import cn.com.cifi.mars.util.LocalDateTimeUtils;
import cn.com.cifi.mars.util.MyMapUtils;
import cn.com.cifi.mars.util.StrUtils;
import cn.com.cifi.mars.util.SysLogUtils;
import cn.com.cifi.mars.util.UUIDUtils;
import cn.com.cifi.mars.util.VersionNameUtils;
import cn.com.cifi.mars.util.VersionUtils;
import lombok.extern.slf4j.Slf4j;

/**
 * <p>
 * 回款计划版本表 服务实现类
 * </p>
 *
 * @author JingYuanXiang
 * @since 2020-11-06
 */
@Slf4j
@Service
public class ReturnPlanVersionServiceImpl extends ServiceImpl<ReturnPlanVersionMapper, ReturnPlanVersion> implements ReturnPlanVersionService {

    @Autowired
    private ReturnPlanVersionMapper returnPlanVersionMapper;
    @Autowired
    private ReturnPlanVersionWrapper returnPlanVersionWrapper;
    @Autowired
    private ReturnPlanVersionDetailService returnPlanVersionDetailService;
    @Autowired
    private ReturnPlanVersionDetailMapper returnPlanVersionDetailMapper;
    @Autowired
    private ReturnPlanVersionDetailWrapper returnPlanVersionDetailWrapper;
    @Autowired
    private ReturnPlanVersionCoefficientService returnPlanVersionCoefficientService;
    @Autowired
    private ReturnPlanVersionCoefficientMapper returnPlanVersionCoefficientMapper;
    @Autowired
    ProjectReturnCoefficientMapper projectReturnCoefficientMapper;
    @Autowired
    private SignPlanVersionService signPlanVersionService;
    @Autowired
    private SignPlanVersionMapper signPlanVersionMapper;
    @Autowired
    private SignPlanVersionWrapper signPlanVersionWrapper;
    @Autowired
    private SignPlanVersionSupplyDetailMapper signPlanVersionSupplyDetailMapper;
    @Autowired
    private PriceVersionWrapper priceVersionWrapper;
    @Autowired
    private ProjectByStagesService projectByStagesService;
    @Autowired
    private ProjectByStagesMapper projectByStagesMapper;
    @Autowired
    private SubmitApprovedProjectService submitApprovedProjectService;
    @Autowired
    private SysProjectFetchNumberService sysProjectFetchNumberService;
    @Autowired
    private IdmUserService idmUserService;
    @Autowired
    private CommonService commonService;
    @Autowired
    private PortalTodoMapper portalTodoMapper;
    @Autowired
    private PortalTodoTaskMapper portalTodoTaskMapper;
    @Autowired
    private ApprovalBillService approvalBillService;
    @Autowired
    private ProjectMapper projectMapper;
    @Autowired
    private ProjectBasicService projectBasicService;
    @Autowired
    private ApprovalPreviewService approvalPreviewService;
    @Autowired
    private FileUploadService fileUploadService;
    @Autowired
    private MymRoomsService mymRoomsService;
    @Autowired
    private VersionLogService versionLogService;
    @Autowired
    private SignPlanVersionDetailService signPlanVersionDetailService;
    @Autowired
    private DmCityReturnCoefficientMapper dmCityReturnCoefficientMapper;
    @Autowired
    private SupplyPlanVersionDetailMapper supplyPlanVersionDetailMapper;
    @Autowired
    private StageMapper stageMapper;
    @Autowired
	private BudgetLockRecordService budgetLockRecordService;
    @Autowired
   	private BudgetVersionIdentifyService budgetVersionIdentifyService;

    @Value("${mobile_task_url}")
    private String returnNoticeAppUrl;

    @Value("${return_notice_pc_url}")
    private String returnNoticePcUrl;

    /**
     * 获取新建来源数据
     * @param stageId 分期id
     * @param versionType 版本类型
     * @return
     */
    @Override
    public ReturnCreateSourceDataVo getCreateSourceData(String stageId, String versionType) {
        //参数校验
        if(StringUtils.isBlank(stageId) || StringUtils.isBlank(versionType)){
            throw new MsgException(CodeEnum.REQUEST_PARAM_NOT_FULL_ERROR);
        }
        //查询分期数据
        ProjectByStagesDto stage = projectByStagesMapper.selectByOrgdepid(stageId);
        if (null == stage) {
            throw new MsgException(CodeEnum.STAGE_NON_EXISTENT);
        }
        //校验是否符合创建回款计划通用规则
        whetherCreate(stage);

        //获取取数签约版本数据
        SignPlanVersion signPlanVersion = signPlanVersionWrapper.selectLatestByStageIdAndVersionTypeAndVersionState(stage.getOrgdepid(), versionType, VersionStatusEnum.SUCCESSAPPROVED.getKey(), IsDeleteEnum.NO.getKey());
        if(null == signPlanVersion){
            throw new MsgException(CodeEnum.NOT_SIGN_PLAN_VERSION);
        }
        
        //签约计划版本名称
        String signVersionName = VersionNameUtils.getVersionName(signPlanVersion);
        //获取创建回款计划版本来源回款系数名称
        String returnCoefficientName = returnPlanVersionCoefficientService.getCreateSourceCoefficient(stage.getParentOrgid(),signPlanVersion.getId());
        //分期取数系统
        SysProjectFetchNumber sysProjectFetchNumber = sysProjectFetchNumberService.selectByFid(stageId);
        String projectFetchNumberName = "";
        if(sysProjectFetchNumber == null) {
        	 if(VersionTypeEunm.INVESTMENT.getKey().equals(signPlanVersion.getVersionType())){
                 projectFetchNumberName = null;
             }else {
            	 throw new MsgException(CodeEnum.SYS_PROJECT_FETCH_ERROR);
             }
        }else {
        	if(StringUtils.isBlank(sysProjectFetchNumber.getCountingSystemId()) && !VersionTypeEunm.INVESTMENT.getKey().equals(signPlanVersion.getVersionType())) {
        		throw new MsgException(CodeEnum.SYS_PROJECT_FETCH_ERROR);
        	}else {
        		 projectFetchNumberName = FetchNnumberEnum.getValueByKey(sysProjectFetchNumber.getCountingSystemId());
        	}
        }
        ReturnCreateSourceDataVo vo = new ReturnCreateSourceDataVo();
        vo.setSignPlanVersionName(signVersionName);
        vo.setReturnCoefficientName(returnCoefficientName);
        vo.setProjectFetchNumberName(StringUtils.isBlank(projectFetchNumberName) ? "无": projectFetchNumberName);
        return vo;
    }

    /**
     * 创建回款计划
     * @param param 回款计划创建参数
     * @param user 用户
     */
    @Transactional
    @Override
    public void create(ReturnPlanCreateParam param,IdmUser user) {
        //参数校验
        if(null == param || StringUtils.isBlank(param.getStageId()) || StringUtils.isBlank(param.getVersionType()) || null == user){
            throw new MsgException(CodeEnum.REQUEST_PARAM_NOT_FULL_ERROR);
        }
        //查询分期数据
        ProjectByStagesDto stage = projectByStagesMapper.selectByOrgdepid(param.getStageId());
        if (null == stage) {
            throw new MsgException(CodeEnum.STAGE_NON_EXISTENT);
        }

        //校验是否符合创建回款计划通用规则
        whetherCreate(stage);
        //校验是否符合各版本类型对应的创建回款计划规则
        whetherCreateByType(param.getStageId(),param.getVersionType());

        ReturnPlanVersion returnPlanVersion = null;
        if(VersionTypeEunm.DYNAMIC.getKey().equals(param.getVersionType())){
            //获取最新签约计划版本
            SignPlanVersion signPlanVersion = signPlanVersionWrapper.selectLatestByStageIdAndVersionTypeAndVersionState(stage.getOrgdepid(), VersionTypeEunm.DYNAMIC.getKey(), VersionStatusEnum.SUCCESSAPPROVED.getKey(), IsDeleteEnum.NO.getKey());
            if(null == signPlanVersion){
                throw new MsgException(CodeEnum.NOT_SIGN_PLAN_VERSION);
            }
            //判断是否售罄版本
            boolean isSoldOutVersion = signPlanVersionService.getIsSoldOutVersion(signPlanVersion);
            if(isSoldOutVersion) {
                //创建售罄回款计划版本
                returnPlanVersion = this.createSoldOutReturnPlanVersion(signPlanVersion, VersionCreateEnum.MANUALCREATE.getKey(), user);
            }
        }

        if(null == returnPlanVersion){
            //非售罄分期,按照正常创建回款计划版本
            returnPlanVersion = this.createReturnPlanVersion(stage, param.getVersionType(),param.getVersionIdentify(),VersionCreateEnum.MANUALCREATE.getKey(), user);
        }
        //创建回款计划版本系数列表
        returnPlanVersionCoefficientService.createList(returnPlanVersion, user);
        //创建回款计划详情列表
        returnPlanVersionDetailService.createList(returnPlanVersion);

        //消除代办
        deleteTodoInfo(param.getStageId() + param.getVersionType(),OperatorTypeEnum.TRANSFERRED_TO_OFFICE.getKey(),user);
    }

    /**
     * 异常创建回款计划
     * @param param 回款计划创建参数
     * @param user 用户
     */
    @Override
    @Transactional
    public void exceptionCreate(ReturnPlanCreateParam param, IdmUser user) {
        //参数校验
        if(null == param || StringUtils.isBlank(param.getStageId()) || StringUtils.isBlank(param.getVersionType()) || null == user){
            throw new MsgException(CodeEnum.REQUEST_PARAM_NOT_FULL_ERROR);
        }
        //查询分期数据
        ProjectByStagesDto stage = projectByStagesMapper.selectByOrgdepid(param.getStageId());
        if (null == stage) {
            throw new MsgException(CodeEnum.STAGE_NON_EXISTENT);
        }

        //校验是否符合创建回款计划通用规则
        whetherCreate(stage);

        //创建回款计划版本
        ReturnPlanVersion returnPlanVersion = this.createReturnPlanVersion(stage, param.getVersionType(), param.getVersionIdentify(),VersionCreateEnum.EXCEPTIONCREATE.getKey(), user);
        //创建回款计划版本系数列表
        returnPlanVersionCoefficientService.createList(returnPlanVersion, user);
        //创建回款计划详情列表
        returnPlanVersionDetailService.createList(returnPlanVersion);

        //消除代办
        deleteTodoInfo(param.getStageId() + param.getVersionType(),OperatorTypeEnum.TRANSFERRED_TO_OFFICE.getKey(),user);
    }

    private ReturnPlanVersion createSoldOutReturnPlanVersion(SignPlanVersion signPlanVersion, String versionCreate, IdmUser user) {

        LocalDateTime now = LocalDateTime.now();
        //版本归属年月
        String affiliationMonth = LocalDateTimeUtils.formatTime(now, DateUtils.DATE_JFP_STR);

        String id = UUIDUtils.create();
        //项目id
        String projectId = signPlanVersion.getProjectId();
        //分期id
        String stageId = signPlanVersion.getProjectfId();
        //版本号
        String versionNum = getVersionNum(stageId,VersionTypeEunm.DYNAMIC.getKey(),null, affiliationMonth);
        //分期取数系统
        SysProjectFetchNumber sysProjectFetchNumber = sysProjectFetchNumberService.selectByFid(stageId);
        //是否进入一房一价
        int isContainSale = 0;
        List<MymRooms> rooms = mymRoomsService.selectNewestWarPlanByFid(stageId);
        if(CollectionUtils.isNotEmpty(rooms)){
            isContainSale =  1;
        }

        ReturnPlanVersion returnPlanVersion = new ReturnPlanVersion();
        returnPlanVersion.setId(id);
        returnPlanVersion.setVersionType(VersionTypeEunm.DYNAMIC.getKey());
        returnPlanVersion.setVersionNum(versionNum);
        returnPlanVersion.setProjectId(projectId);
        returnPlanVersion.setStageId(stageId);
        returnPlanVersion.setVersionState(VersionStatusEnum.NOTPREPARED.getKey());
        returnPlanVersion.setSignPlanVersionId(signPlanVersion.getId());
        returnPlanVersion.setSupplyPlanVersionId(signPlanVersion.getSourceSupplyId());
        returnPlanVersion.setResourceMatching(null);
        returnPlanVersion.setAnnualObjectives(null);
        returnPlanVersion.setCompileYear(null);
        returnPlanVersion.setVersionIdentify(null);
        returnPlanVersion.setSourceVersionId(null);
        returnPlanVersion.setIsAssessment(WhetherEnum.NO.getKey());
        returnPlanVersion.setAffiliationMonth(affiliationMonth);
        returnPlanVersion.setDataSource(sysProjectFetchNumber.getCountingSystemId());
        returnPlanVersion.setIsFinalization(WhetherEnum.NO.getKey());
        returnPlanVersion.setCreateBy(user.getAlias());
        returnPlanVersion.setCreateTime(now);
        returnPlanVersion.setVersionCreate(versionCreate);
        returnPlanVersion.setUpdateBy(user.getAlias());
        returnPlanVersion.setUpdateTime(now);
        returnPlanVersion.setIsDelete(IsDeleteEnum.NO.getKey());
        returnPlanVersion.setDescription(null);
        returnPlanVersion.setIsStageNewest(WhetherEnum.NO.getKey());
        returnPlanVersion.setIsContainSale(isContainSale);
        returnPlanVersionMapper.insert(returnPlanVersion);

        saveVersionLog(Arrays.asList(id),VersionEventEnum.MANUALCREATE.getKey(),"",user.getAlias(),"新建回款计划",null);
        return returnPlanVersion;
    }

    /**
     * 查询分期最新版本类型
     * @param stageId 分期id
     * @return
     */
    @Override
    public String getNewVersionType(String stageId) {
        return returnPlanVersionMapper.selectNewVersionTypeByStageId(stageId,IsDeleteEnum.NO.getKey());
    }


    /**
     * 自动创建回款计划
     * @param stageId 分期id
     * @param versionType 版本类型
     */
    public ReturnPlanVersion automaticallyCreate(String stageId, String versionType,String versionIdentify) {
            //参数校验
            if(StringUtils.isBlank(stageId) || StringUtils.isBlank(versionType)){
                throw new MsgException(CodeEnum.REQUEST_PARAM_NOT_FULL_ERROR);
            }
            //查询分期数据
            ProjectByStagesDto stage = projectByStagesMapper.selectByOrgdepid(stageId);
            if (null == stage) {
                throw new MsgException(CodeEnum.STAGE_NON_EXISTENT);
            }
            IdmUser user = new IdmUser();
            user.setAlias(Constants.SYS);

            //校验是否符合创建回款计划通用规则
            whetherCreate(stage);

            //创建回款计划版本
            ReturnPlanVersion returnPlanVersion = this.createReturnPlanVersion(stage,versionType, versionIdentify,VersionCreateEnum.AUTOMATICCREATE.getKey(), user);
            //创建回款计划版本系数列表
            returnPlanVersionCoefficientService.createList(returnPlanVersion, user);
            //创建回款计划详情列表
            returnPlanVersionDetailService.createList(returnPlanVersion);
            return returnPlanVersion;

    }

    /**
     * 查询回款计划版本数据列表
     * @param stageId 分期id
     * @param versionType 版本类型
     * @return
     */
    @Override
    public List<ReturnPlanVersionDataVo> getReturnPlanVersionDataList(String stageId, String versionType) {
        //参数校验
        if(StringUtils.isBlank(stageId) || StringUtils.isBlank(versionType)){
            throw new MsgException(CodeEnum.REQUEST_PARAM_NOT_FULL_ERROR);
        }
        // 1、查询分期下该版本的签约计划版本列表
        List<ReturnPlanVersion> returnPlanVersionList = returnPlanVersionWrapper.selectByStageIdAndVersionType(stageId, versionType, IsDeleteEnum.NO.getKey());
        if (CollectionUtils.isEmpty(returnPlanVersionList)) {
            return null;
        }
        // 2、生成版本全路径列表
        List<ReturnPlanVersionDataVo> voList = new ArrayList<>();
        for (ReturnPlanVersion returnPlanVersion : returnPlanVersionList) {
            ReturnPlanVersionDataVo vo = this.getVersionFullNameVo(returnPlanVersion);
            voList.add(vo);
        }
        return voList;
    }

    /**
     * 查看回款计划年份列表
     * @param versionId 回款计划版本id
     * @return
     */
    @Override
    public List<String> getYearList(String versionId) {
        //获取年份列表
        return returnPlanVersionDetailMapper.selectYearListByVersionIdList(Arrays.asList(versionId),IsDeleteEnum.NO.getKey());
    }


    /**
     * 查看回款计划一年数据
     * @param versionId 回款计划版本id
     * @param year 年份
     * @return
     */
    @Override
    public List<ReturnPlanYearDataVo> getAnnualData(String versionId, String year) {
        //参数校验
        if(StringUtils.isBlank(versionId) || StringUtils.isBlank(year)){
            throw new MsgException(CodeEnum.REQUEST_PARAM_NOT_FULL_ERROR);
        }
        ReturnPlanVersion returnPlanVersion = returnPlanVersionMapper.selectById(versionId);
        //获取回款计划一年数据
        List<ReturnPlanYearDataVo> voList = getAnnualDataByVersion(returnPlanVersion, year);

        // 类型排序
        return typeOrderBy(voList);
    }


    /**
     * 查看回款计划一年数据
     * @param returnPlanVersion 回款计划版本
     * @param year 年份
     * @return
     */
    public List<ReturnPlanYearDataVo> getAnnualDataByVersion(ReturnPlanVersion returnPlanVersion, String year) {
        if(null == returnPlanVersion || returnPlanVersion.getIsDelete() == IsDeleteEnum.YES.getKey()){
            throw new MsgException(CodeEnum.NOT_RETURN_PLAN_VERSION_ERROR);
        }
        int yearInt = Integer.valueOf(year);

        String versionId = returnPlanVersion.getId();
        ProjectByStagesDto stage = projectByStagesMapper.selectByOrgdepid(returnPlanVersion.getStageId());
        if (null == stage) {
            throw new MsgException(CodeEnum.STAGE_NON_EXISTENT);
        }
        //所属时间
        LocalDateTime affiliationTime = LocalDateTimeUtils.toLocalDateTime(returnPlanVersion.getAffiliationMonth());

        List<ReturnPlanYearDataVo> voList = new ArrayList<>();
        List<ReturnPlanYearDataVo> groupVoList = new ArrayList<>();
        List<ReturnPlanYearDataVo> stageVoList = new ArrayList<>();

        //获取组合业态数据
        CombinationProductDataBO combinationProductData = getCombinationProductData(returnPlanVersion.getSignPlanVersionId());

        //获取回款计划详情列表
        List<ReturnPlanVersionDetail> returnPlanDetailList = returnPlanVersionDetailWrapper.selectByVersionId(versionId,IsDeleteEnum.NO.getKey());
        //以组团id和组合业态分组,(key:组团ID，(key:组合业态编码，value:回款计划详情))
        Map<String, Map<String, List<ReturnPlanVersionDetail>>> returnPlanDetailByGroupIdMap = returnPlanDetailList.stream()
                .collect(Collectors.groupingBy(ReturnPlanVersionDetail::getGroupId, Collectors.groupingBy(ReturnPlanVersionDetail::getCombinationProductCode)));

        for (String groupId : returnPlanDetailByGroupIdMap.keySet()){
            groupVoList = new ArrayList<>();
            String groupNmae = null;

            Map<String, List<ReturnPlanVersionDetail>> returnPlanDetailMap = returnPlanDetailByGroupIdMap.get(groupId);
            for (String combinationProductCode : returnPlanDetailMap.keySet()){
                List<ReturnPlanVersionDetail> returnPlanDetails = returnPlanDetailMap.get(combinationProductCode);
                ReturnPlanVersionDetail returnPlanDetail = returnPlanDetails.get(0);

                ReturnPlanYearDataVo vo = new ReturnPlanYearDataVo();
                vo.setId(returnPlanDetail.getId());
                vo.setPid(returnPlanDetail.getGroupId());
                vo.setType(OrgTypeEunm.ORG_PRODUCT.getKey());
                vo.setName(returnPlanDetail.getProductName()+ "-"+ returnPlanDetail.getBusinessTypeName()
                        + "-" + returnPlanDetail.getFreeTypeName()+ "-" + returnPlanDetail.getProTypeName());
                vo.setIsEstablishment(WhetherEnum.NO.getKey());
                //获取结转日期
                LocalDateTime carryOverTime = combinationProductData.getCarryOverTime(groupId,combinationProductCode);
                vo.setCarryOverTimeStr(null == carryOverTime ? null : LocalDateTimeUtils.formatTime(carryOverTime, DateUtils.DATE_SMALL_STR));

                //获取总货值
                BigDecimal totalValue = combinationProductData.getTotalValue(groupId,combinationProductCode);
                //获取签约与回款
                SignAndReturnVo signAndReturn = getSignAndReturn(totalValue,affiliationTime,returnPlanDetails);
                //获取年回款计划详情列表
                List<ReturnPlanVersionDetail> yearReturnPlanDetailList = returnPlanDetails.stream()
                        .filter(s -> yearInt == s.getReturnTime().getYear())
                        .sorted(Comparator.comparing(ReturnPlanVersionDetail::getReturnTime))
                        .collect(Collectors.toList());


                //回款详情数据补充
                ReturnPlanVersionDetail earlyMonthData = getEarlyMonthDataSupplement(returnPlanDetails,yearInt);
                ReturnPlanVersionDetail finalMonthData = getFinalMonthDataSupplement(returnPlanDetails,yearInt);
                //获取年度数据
                ReturnPlanDetailYearData yearData = getYearData(yearInt, affiliationTime,yearReturnPlanDetailList,earlyMonthData, finalMonthData);
                //获取月度数据列表
                List<ReturnPlanDetailMonthData> monthDataList = getMonthDataList(yearInt,affiliationTime,yearReturnPlanDetailList,earlyMonthData, finalMonthData);

                vo.setSignAndReturn(signAndReturn);
                vo.setYearData(yearData);
                vo.setMonthDataList(monthDataList);
                groupVoList.add(vo);
                voList.add(vo);
                groupNmae = returnPlanDetail.getGroupName();
            }
            ReturnPlanYearDataVo vo = new ReturnPlanYearDataVo();
            vo.setId(groupId);
            vo.setPid(returnPlanVersion.getStageId());
            vo.setType(OrgTypeEunm.ORG_GROUP.getKey());
            vo.setName(groupNmae);
            //该组团是否编制完成
            vo.setIsEstablishment(getIsEstablishment(returnPlanDetailMap));
            //汇总计算签约与回款
            SignAndReturnVo signAndReturn = summarySignAndReturn(groupVoList);
            //汇总年度数据
            ReturnPlanDetailYearData yearData = summaryYearData(groupVoList);
            //汇总计算月度数据列表
            List<ReturnPlanDetailMonthData> monthDataList = summaryMonthDataList(groupVoList);
            vo.setSignAndReturn(signAndReturn);
            vo.setYearData(yearData);
            vo.setMonthDataList(monthDataList);
            stageVoList.add(vo);
            voList.add(vo);
        }
        ReturnPlanYearDataVo vo = new ReturnPlanYearDataVo();
        vo.setId(stage.getOrgdepid());
        vo.setPid("0");
        vo.setType(OrgTypeEunm.ORG_STAGE.getKey());
        vo.setName(stage.getOrgname());
        //汇总计算签约与回款
        SignAndReturnVo signAndReturn = summarySignAndReturn(stageVoList);
        //汇总年度数据
        ReturnPlanDetailYearData yearData = summaryYearData(stageVoList);
        //汇总计算月度数据列表
        List<ReturnPlanDetailMonthData> monthDataList = summaryMonthDataList(stageVoList);

        vo.setSignAndReturn(signAndReturn);
        vo.setYearData(yearData);
        vo.setMonthDataList(monthDataList);
        voList.add(vo);
        return voList;
    }

    /**
     * 回款详情数据补充(组合业态数据在全周期中部分月数据不存在)
     * @param returnPlanDetailList
     * @param year
     * @return
     */
    @Override
    public ReturnPlanVersionDetail getEarlyMonthDataSupplement(List<ReturnPlanVersionDetail> returnPlanDetailList, int year) {
        ReturnPlanVersionDetail detail = new ReturnPlanVersionDetail();

        //月初应收账款
        BigDecimal earlyReceivableAmount = BigDecimal.ZERO;
        //1月月初应收账款
        BigDecimal oneEarlyReceivableAmount = BigDecimal.ZERO;
        //月度已签未回回款数
        BigDecimal signedUnrecovered = BigDecimal.ZERO;
        //月末应收账款
        BigDecimal finalReceivable = BigDecimal.ZERO;
        //累计年度签约合计
        BigDecimal yearSignAmountTotal = BigDecimal.ZERO;
        //累计年度回款合计
        BigDecimal yearReturnAmountTotal = BigDecimal.ZERO;
        //累计签约合计
        BigDecimal signAmountTotal = BigDecimal.ZERO;
        //累计回款合计
        BigDecimal returnAmountTotal = BigDecimal.ZERO;
        //年度综合回款率
        BigDecimal annualOverallReturnRate = BigDecimal.ZERO;
        //累计签约回款率
        BigDecimal signReturnRate = BigDecimal.ZERO;

        ReturnPlanVersionDetail finalDetail = returnPlanDetailList.stream()
                .filter(s -> year > s.getReturnTime().getYear())
                .max(Comparator.comparing(ReturnPlanVersionDetail::getReturnTime)).orElse(null);
        ReturnPlanVersionDetail earlyMonthDetail = returnPlanDetailList.stream()
                .filter(s -> year == s.getReturnTime().getYear() && 1 == s.getReturnTime().getMonthValue())
                .max(Comparator.comparing(ReturnPlanVersionDetail::getReturnTime)).orElse(null);
        if (null != earlyMonthDetail) {
            earlyReceivableAmount = earlyMonthDetail.getEarlyReceivableAmount();
            signedUnrecovered = earlyMonthDetail.getSignedUnrecovered();
            finalReceivable = earlyMonthDetail.getFinalReceivable();
            signAmountTotal = earlyMonthDetail.getSignAmountTotal();
            returnAmountTotal = earlyMonthDetail.getReturnAmountTotal();
            oneEarlyReceivableAmount = earlyMonthDetail.getEarlyReceivableAmount();
            yearSignAmountTotal = earlyMonthDetail.getYearSignAmountTotal();
            yearReturnAmountTotal = earlyMonthDetail.getYearReturnAmountTotal();
        }else {
            if(null != finalDetail){
                earlyReceivableAmount = finalDetail.getFinalReceivable();
                signedUnrecovered = finalDetail.getSignedUnrecovered();
                finalReceivable = finalDetail.getFinalReceivable();
                signAmountTotal = finalDetail.getSignAmountTotal();
                returnAmountTotal= finalDetail.getReturnAmountTotal();
                oneEarlyReceivableAmount = finalDetail.getFinalReceivable();
            }
        }
        annualOverallReturnRate = BigDecimalUtils.divBigDecimal(yearReturnAmountTotal,oneEarlyReceivableAmount.add(yearSignAmountTotal),5);;
        signReturnRate = BigDecimalUtils.divBigDecimal(returnAmountTotal,signAmountTotal,5);;

        detail.setEarlyReceivableAmount(earlyReceivableAmount);
        detail.setSignedUnrecovered(signedUnrecovered);
        detail.setFinalReceivable(finalReceivable);
        detail.setYearSignAmountTotal(yearSignAmountTotal);
        detail.setYearReturnAmountTotal(yearReturnAmountTotal);
        detail.setSignAmountTotal(signAmountTotal);
        detail.setReturnAmountTotal(returnAmountTotal);
        detail.setAnnualOverallReturnRate(annualOverallReturnRate);
        detail.setSignReturnRate(signReturnRate);
        return detail;
    }

    /**
     * 回款详情数据补充(组合业态数据在全周期中部分月数据不存在)
     * @param returnPlanDetailList
     * @param year
     * @return
     */
    @Override
    public ReturnPlanVersionDetail getFinalMonthDataSupplement(List<ReturnPlanVersionDetail> returnPlanDetailList, int year) {
        ReturnPlanVersionDetail detail = new ReturnPlanVersionDetail();

        //月初应收账款
        BigDecimal earlyReceivableAmount = BigDecimal.ZERO;
        //1月月初应收账款
        BigDecimal oneEarlyReceivableAmount = BigDecimal.ZERO;
        //月度已签未回回款数
        BigDecimal signedUnrecovered = BigDecimal.ZERO;
        //月末应收账款
        BigDecimal finalReceivable = BigDecimal.ZERO;
        //累计年度签约合计
        BigDecimal yearSignAmountTotal = BigDecimal.ZERO;
        //累计年度回款合计
        BigDecimal yearReturnAmountTotal = BigDecimal.ZERO;
        //累计签约合计
        BigDecimal signAmountTotal = BigDecimal.ZERO;
        //累计回款合计
        BigDecimal returnAmountTotal = BigDecimal.ZERO;
        //年度综合回款率
        BigDecimal annualOverallReturnRate = BigDecimal.ZERO;
        //累计签约回款率
        BigDecimal signReturnRate = BigDecimal.ZERO;

        //查看当年是否有数据
        if(returnPlanDetailList.stream().anyMatch(s -> year >= s.getReturnTime().getYear())){
            earlyReceivableAmount = returnPlanDetailList.stream()
                    .filter(s -> year >= s.getReturnTime().getYear())
                    .max(Comparator.comparing(ReturnPlanVersionDetail :: getReturnTime))
                    .get()
                    .getFinalReceivable();
            signedUnrecovered = returnPlanDetailList.stream()
                    .filter(s -> year >= s.getReturnTime().getYear())
                    .max(Comparator.comparing(ReturnPlanVersionDetail :: getReturnTime))
                    .get()
                    .getSignedUnrecovered();
            finalReceivable = returnPlanDetailList.stream()
                    .filter(s -> year >= s.getReturnTime().getYear())
                    .max(Comparator.comparing(ReturnPlanVersionDetail :: getReturnTime))
                    .get()
                    .getFinalReceivable();
            signAmountTotal = returnPlanDetailList.stream()
                    .filter(s -> year >= s.getReturnTime().getYear())
                    .max(Comparator.comparing(ReturnPlanVersionDetail :: getReturnTime))
                    .get()
                    .getSignAmountTotal();
            returnAmountTotal= returnPlanDetailList.stream()
                    .filter(s -> year >= s.getReturnTime().getYear())
                    .max(Comparator.comparing(ReturnPlanVersionDetail :: getReturnTime))
                    .get()
                    .getReturnAmountTotal();

        }
        if(returnPlanDetailList.stream().anyMatch(s -> year == s.getReturnTime().getYear())){
            yearSignAmountTotal = returnPlanDetailList.stream()
                    .filter(s -> year == s.getReturnTime().getYear())
                    .max(Comparator.comparing(ReturnPlanVersionDetail :: getReturnTime))
                    .get()
                    .getYearSignAmountTotal();
            yearReturnAmountTotal = returnPlanDetailList.stream()
                    .filter(s -> year == s.getReturnTime().getYear())
                    .max(Comparator.comparing(ReturnPlanVersionDetail :: getReturnTime))
                    .get()
                    .getYearReturnAmountTotal();
        }
        if(returnPlanDetailList.stream().anyMatch(s -> year == s.getReturnTime().getYear() && 1 == s.getReturnTime().getMonthValue())){
            oneEarlyReceivableAmount = returnPlanDetailList.stream()
                    .filter(s -> year == s.getReturnTime().getYear() && 1 == s.getReturnTime().getMonthValue())
                    .max(Comparator.comparing(ReturnPlanVersionDetail :: getReturnTime))
                    .get()
                    .getEarlyReceivableAmount();
        }
        annualOverallReturnRate = BigDecimalUtils.divBigDecimal(yearReturnAmountTotal,oneEarlyReceivableAmount.add(yearSignAmountTotal),5);;
        signReturnRate = BigDecimalUtils.divBigDecimal(returnAmountTotal,signAmountTotal,5);;

        detail.setEarlyReceivableAmount(earlyReceivableAmount);
        detail.setSignedUnrecovered(signedUnrecovered);
        detail.setFinalReceivable(finalReceivable);
        detail.setYearSignAmountTotal(yearSignAmountTotal);
        detail.setYearReturnAmountTotal(yearReturnAmountTotal);
        detail.setSignAmountTotal(signAmountTotal);
        detail.setReturnAmountTotal(returnAmountTotal);
        detail.setAnnualOverallReturnRate(annualOverallReturnRate);
        detail.setSignReturnRate(signReturnRate);
        return detail;
    }

    /**
     * 类型排序
     * @param returnPlanYearDataVoList
     */
    private List<ReturnPlanYearDataVo> typeOrderBy(List<ReturnPlanYearDataVo> returnPlanYearDataVoList) {
        List<ReturnPlanYearDataVo> voList = new ArrayList<>();

        List<ReturnPlanYearDataVo> groupVoList = new ArrayList<>();
        List<ReturnPlanYearDataVo> stageVoList = new ArrayList<>();
        List<ReturnPlanYearDataVo> vos = new ArrayList<>();
        for(ReturnPlanYearDataVo returnPlanYearDataVo : returnPlanYearDataVoList){
            String type = returnPlanYearDataVo.getType();
            if(OrgTypeEunm.ORG_GROUP.getKey().equals(type)){
                groupVoList.add(returnPlanYearDataVo);
            }else if(OrgTypeEunm.ORG_STAGE.getKey().equals(type)){
                stageVoList.add(returnPlanYearDataVo);
            }else {
                vos.add(returnPlanYearDataVo);
            }
        }

        groupVoList = ListUtils.sortByStrList(groupVoList,ListUtils.groupNameList, ReturnPlanYearDataVo::getName);
        stageVoList = ListUtils.sortByStrList(stageVoList,ListUtils.stageNameList,s->s.getName().substring(0,s.getName().indexOf("(")));
        voList.addAll(stageVoList);
        voList.addAll(groupVoList);
        voList.addAll(vos);
        return voList;
    }

    /**
     * 该组团是否编制完成
     * @param returnPlanDetailMap 回款计划详情Map
     * @return
     */
    private int getIsEstablishment(Map<String, List<ReturnPlanVersionDetail>> returnPlanDetailMap) {
        List<List<ReturnPlanVersionDetail>> detailList = returnPlanDetailMap.values().stream().filter(a -> {
            List<ReturnPlanVersionDetail> details = a.stream()
                    .filter(b -> WhetherEnum.NO.getKey() == b.getIsEstablishment()).collect(Collectors.toList());
            return CollectionUtils.isNotEmpty(details);
        }).collect(Collectors.toList());

        if(CollectionUtils.isEmpty(detailList)){
            return WhetherEnum.YES.getKey();
        }else {
            return WhetherEnum.NO.getKey();
        }
    }

    /**
     * 获取签约与回款
     * @param totalValue 总货值
     * @param affiliationTime 版本归属时间
     * @param returnPlanDetailList 回款计划详情列表
     * @return
     */
    @Override
    public SignAndReturnVo getSignAndReturn(BigDecimal totalValue, LocalDateTime affiliationTime, List<ReturnPlanVersionDetail> returnPlanDetailList) {
        SignAndReturnVo vo = new SignAndReturnVo();

        //累计已售
        BigDecimal accumulativeSold = BigDecimal.ZERO;
        //累计已回款
        BigDecimal accumulativeReturn = BigDecimal.ZERO;
        for(ReturnPlanVersionDetail detail : returnPlanDetailList){
            if(LocalDateTimeUtils.compareDate(detail.getReturnTime(),affiliationTime,0)){
                accumulativeSold = accumulativeSold.add(detail.getActualSignAmount());
                accumulativeReturn = accumulativeReturn.add(detail.getIncrementReturnAmountActual().add(detail.getStoreReturnAmountActual()));
            }
        }
        //已签未回(累计已售 - 累计已回款)
        BigDecimal signNotReturn = accumulativeSold.subtract(accumulativeReturn);
        //未签未回(总货值 - 累计已售)
        BigDecimal notSignNotReturn = totalValue.subtract(accumulativeSold);
        //累计签约回款率(累计已回款/累计已售)
        BigDecimal signReturnRate = BigDecimalUtils.divBigDecimal(accumulativeReturn,accumulativeSold,5);

        vo.setTotalValue(totalValue);
        vo.setAccumulativeSold(accumulativeSold);
        vo.setAccumulativeReturn(accumulativeReturn);
        vo.setSignNotReturn(signNotReturn);
        vo.setNotSignNotReturn(notSignNotReturn);
        vo.setSignReturnRate(signReturnRate);
        return vo;
    }

    /**
     * 获取年度数据
     *
     * @param year
     * @param affiliationTime 版本归属时间
     * @param returnPlanDetailList 回款计划详情列表
     * @param earlyMonthData
     * @param finalMonthData
     * @return
     */
    @Override
    public ReturnPlanDetailYearData getYearData(int year, LocalDateTime affiliationTime, List<ReturnPlanVersionDetail> returnPlanDetailList, ReturnPlanVersionDetail earlyMonthData, ReturnPlanVersionDetail finalMonthData) {
        //年度签约
        BigDecimal yearSign = BigDecimal.ZERO;
        //年度实际数-增量回款
        BigDecimal annualActualIncrementalReturn  = BigDecimal.ZERO;
        //年度实际数-存量回款
        BigDecimal annualActualStoreReturn  = BigDecimal.ZERO;
        //年度测算数-增量回款
        BigDecimal annualCalculateIncrementalReturn  = BigDecimal.ZERO;
        //年度测算数-存量回款
        BigDecimal annualCalculateStoreReturn  = BigDecimal.ZERO;
        //年度调整数-增量回款
        BigDecimal annualAdjustIncrementalReturn  = BigDecimal.ZERO;
        //年度调整数-存量回款
        BigDecimal annualAdjustStoreReturn  = BigDecimal.ZERO;
        //年度回款合计-增量回款
        BigDecimal annualTotalIncrementalReturn  = BigDecimal.ZERO;
        //年度回款合计-存量回款
        BigDecimal annualTotalStoreReturn  = BigDecimal.ZERO;
        //年度回款合计-回款合计
        BigDecimal annualTotalReturnTotal  = BigDecimal.ZERO;
        for(ReturnPlanVersionDetail detail : returnPlanDetailList){
            if(LocalDateTimeUtils.compareDate(detail.getReturnTime(),affiliationTime,0)){
                yearSign = yearSign.add(detail.getSignAmount());
                annualActualIncrementalReturn = annualActualIncrementalReturn.add(detail.getIncrementReturnAmountActual());
                annualActualStoreReturn = annualActualStoreReturn.add(detail.getStoreReturnAmountActual());
            }else {
                yearSign = yearSign.add(detail.getSignAmount());
                annualCalculateIncrementalReturn = annualCalculateIncrementalReturn.add(detail.getIncrementReturnAmountCalculate());
                annualCalculateStoreReturn = annualCalculateStoreReturn.add(detail.getStoreReturnAmountCalculate());
                annualAdjustIncrementalReturn = annualAdjustIncrementalReturn.add(detail.getIncrementReturnAmountAdjust());
                annualAdjustStoreReturn = annualAdjustStoreReturn.add(detail.getStoreReturnAmountAdjust());
            }
            annualTotalIncrementalReturn = annualTotalIncrementalReturn.add(detail.getIncrementReturnAmount());
            annualTotalStoreReturn = annualTotalStoreReturn.add(detail.getStoreReturnAmount());
            annualTotalReturnTotal = annualTotalReturnTotal.add(detail.getTotalReturnAmount());
        }

        ReturnPlanDetailYearData detailYearData = new ReturnPlanDetailYearData();
        detailYearData.setYear(year);
        detailYearData.setYearSign(yearSign);
        detailYearData.setAnnualActualIncrementalReturn(annualActualIncrementalReturn);
        detailYearData.setAnnualActualStoreReturn(annualActualStoreReturn);
        detailYearData.setAnnualCalculateIncrementalReturn(annualCalculateIncrementalReturn);
        detailYearData.setAnnualCalculateStoreReturn(annualCalculateStoreReturn);
        detailYearData.setAnnualAdjustIncrementalReturn(annualAdjustIncrementalReturn);
        detailYearData.setAnnualAdjustStoreReturn(annualAdjustStoreReturn);
        detailYearData.setAnnualTotalIncrementalReturn(annualTotalIncrementalReturn);
        detailYearData.setAnnualTotalStoreReturn(annualTotalStoreReturn);
        detailYearData.setAnnualTotalReturnTotal(annualTotalReturnTotal);

        //一月回款计划详情
        ReturnPlanVersionDetail oneMonthDetail = CollectionUtils.isEmpty(returnPlanDetailList) ? null : returnPlanDetailList.get(0) ;
        //十二月回款计划详情
        ReturnPlanVersionDetail numMonthDetail = CollectionUtils.isEmpty(returnPlanDetailList) ? null : returnPlanDetailList.get(returnPlanDetailList.size()-1);
        //年初应收账款
        detailYearData.setYearEarlyReceivable(null == oneMonthDetail ? earlyMonthData.getEarlyReceivableAmount() : oneMonthDetail.getEarlyReceivableAmount());
        //年度已签未回回款数
        detailYearData.setYearSignNotReturn(detailYearData.getYearEarlyReceivable());
        //年初应退账款
        detailYearData.setYearEarlyRefund(null == oneMonthDetail ? BigDecimal.ZERO : oneMonthDetail.getRefundable());
        //年度回款合计-年末应收账款
        detailYearData.setAnnualTotalYearEndReceivable(null == numMonthDetail ? finalMonthData.getFinalReceivable() : numMonthDetail.getFinalReceivable());

        return detailYearData;
    }

    /**
     * 获取月度数据列表
     * @param year
     * @param affiliationTime 版本归属时间
     * @param returnPlanDetailList 回款计划详情列表
     * @param earlyMonthData
     * @param finalMonthData
     * @return
     */
    public List<ReturnPlanDetailMonthData> getMonthDataList(int year, LocalDateTime affiliationTime, List<ReturnPlanVersionDetail> returnPlanDetailList, ReturnPlanVersionDetail earlyMonthData, ReturnPlanVersionDetail finalMonthData) {
        List<ReturnPlanDetailMonthData> monthDataList = new ArrayList<>();

        Map<Integer, ReturnPlanVersionDetail> returnPlanDetailMap = returnPlanDetailList.stream()
                .collect(Collectors.toMap(s -> s.getReturnTime().getMonthValue(), s -> s));

        Integer maxMonth = returnPlanDetailList.stream().map(s -> s.getReturnTime().getMonthValue()).max(Comparator.comparing(s -> s)).orElse(12);

        for (int i = 1; i <= 12; i++) {
            ReturnPlanVersionDetail detail = returnPlanDetailMap.get(i);
            ReturnPlanDetailMonthData monthData = new ReturnPlanDetailMonthData();
            monthData.setYear(year);
            monthData.setMonth(i);
            if(null == detail){
                //初始化字段默认值
                if(i <= maxMonth){
                    monthData.initialize(earlyMonthData);
                    monthDataList.add(monthData);
                }else {
                    //初始化字段默认值
                    monthData.initialize(finalMonthData);
                    monthDataList.add(monthData);
                }
                continue;
            }
            //月初应收账款
            monthData.setMonthEarlyReceivable(detail.getEarlyReceivableAmount());
            //月度签约
            monthData.setMonthSign(detail.getSignAmount());
            //月度实际数-增量回款
            monthData.setMonthlyActualIncrementalReturn(detail.getIncrementReturnAmountActual());
            //月度实际数-存量回款
            monthData.setMonthlyActualStoreReturn(detail.getStoreReturnAmountActual());
            //月度测算数-增量回款
            monthData.setMonthlyCalculateIncrementalReturn(detail.getIncrementReturnAmountCalculate());
            //月度测算数-存量回款
            monthData.setMonthlyCalculateStoreReturn(detail.getStoreReturnAmountCalculate());
            //月度调整数-增量回款
            monthData.setMonthlyAdjustIncrementalReturn(detail.getIncrementReturnAmountAdjust());
            //月度调整数-存量回款
            monthData.setMonthlyAdjustStoreReturn(detail.getStoreReturnAmountAdjust());
            //月度已签未回回款数
            monthData.setMonthSignNotReturn(detail.getSignedUnrecovered());
            //期初应退回款数
            monthData.setRefundable(detail.getRefundable());
            //月度回款合计-增量回款
            monthData.setMonthlyTotalIncrementalReturn(detail.getIncrementReturnAmount());
            //月度回款合计-存量回款
            monthData.setMonthlyTotalStoreReturn(detail.getStoreReturnAmount());
            //月度回款合计-回款合计
            monthData.setMonthlyTotalReturnTotal(detail.getTotalReturnAmount());
            //月度回款合计-月末应收账款
            monthData.setMonthlyTotalMonthEndReceivable(detail.getFinalReceivable());
            //累计年度签约合计
            monthData.setYearSignAmountTotal(detail.getYearSignAmountTotal());
            //累计签约合计
            monthData.setSignAmountTotal(detail.getSignAmountTotal());
            //累计年度回款合计
            monthData.setYearReturnAmountTotal(detail.getYearReturnAmountTotal());
            //累计回款合计
            monthData.setReturnAmountTotal(detail.getReturnAmountTotal());
            //累计签约回款率
            monthData.setSignReturnRate(detail.getSignReturnRate());
            //年度综合回款率
            monthData.setAnnualOverallReturnRate(detail.getAnnualOverallReturnRate());
            monthDataList.add(monthData);
        }
        return monthDataList;
    }

    /**
     * 汇总计算签约与回款
     * @param voList 回款计划一年数据列表
     * @return
     */
    private SignAndReturnVo summarySignAndReturn(List<ReturnPlanYearDataVo> voList) {
        SignAndReturnVo vo = new SignAndReturnVo();

        //总货值
        BigDecimal totalValue = BigDecimal.ZERO;
        //累计已售
        BigDecimal accumulativeSold = BigDecimal.ZERO;
        //累计已回款
        BigDecimal accumulativeReturn = BigDecimal.ZERO;
        for(ReturnPlanYearDataVo yearDataVo : voList){
            SignAndReturnVo signAndReturn = yearDataVo.getSignAndReturn();
            totalValue = totalValue.add(signAndReturn.getTotalValue());
            accumulativeSold = accumulativeSold.add(signAndReturn.getAccumulativeSold());
            accumulativeReturn = accumulativeReturn.add(signAndReturn.getAccumulativeReturn());
        }
        //已签未回(累计已售 - 累计已回款)
        BigDecimal signNotReturn = accumulativeSold.subtract(accumulativeReturn);
        //未签未回(总货值 - 累计已售)
        BigDecimal notSignNotReturn = totalValue.subtract(accumulativeSold);
        //累计签约回款率(累计已回款/累计已售)
        BigDecimal signReturnRate = BigDecimalUtils.divBigDecimal(accumulativeReturn,accumulativeSold,5);

        vo.setTotalValue(totalValue);
        vo.setAccumulativeSold(accumulativeSold);
        vo.setAccumulativeReturn(accumulativeReturn);
        vo.setSignNotReturn(signNotReturn);
        vo.setNotSignNotReturn(notSignNotReturn);
        vo.setSignReturnRate(signReturnRate);
        return vo;
    }

    /**
     * 汇总年度数据
     * @param voList 回款计划一年数据列表
     * @return
     */
    private ReturnPlanDetailYearData summaryYearData(List<ReturnPlanYearDataVo> voList) {

        //年度签约
        BigDecimal yearSign = BigDecimal.ZERO;
        //年度实际数-增量回款
        BigDecimal annualActualIncrementalReturn  = BigDecimal.ZERO;
        //年度实际数-存量回款
        BigDecimal annualActualStoreReturn  = BigDecimal.ZERO;
        //年度测算数-增量回款
        BigDecimal annualCalculateIncrementalReturn  = BigDecimal.ZERO;
        //年度测算数-存量回款
        BigDecimal annualCalculateStoreReturn  = BigDecimal.ZERO;
        //年度调整数-增量回款
        BigDecimal annualAdjustIncrementalReturn  = BigDecimal.ZERO;
        //年度调整数-存量回款
        BigDecimal annualAdjustStoreReturn  = BigDecimal.ZERO;
        //年度已签未回回款数
        BigDecimal yearSignNotReturn  = BigDecimal.ZERO;
        //年度回款合计-增量回款
        BigDecimal annualTotalIncrementalReturn  = BigDecimal.ZERO;
        //年度回款合计-存量回款
        BigDecimal annualTotalStoreReturn  = BigDecimal.ZERO;
        //年度回款合计-回款合计
        BigDecimal annualTotalReturnTotal  = BigDecimal.ZERO;
        //年初应收账款
        BigDecimal yearEarlyReceivable = BigDecimal.ZERO;
        //年初应退账款
        BigDecimal yearEarlyRefund = BigDecimal.ZERO;
        //年度回款合计-年末应收账款
        BigDecimal annualTotalYearEndReceivable = BigDecimal.ZERO;

        for(ReturnPlanYearDataVo yearDataVo : voList){
            ReturnPlanDetailYearData yearData = yearDataVo.getYearData();
            yearSign = yearSign.add(yearData.getYearSign());
            annualActualIncrementalReturn = annualActualIncrementalReturn.add(yearData.getAnnualActualIncrementalReturn());
            annualActualStoreReturn = annualActualStoreReturn.add(yearData.getAnnualActualStoreReturn());
            annualCalculateIncrementalReturn = annualCalculateIncrementalReturn.add(yearData.getAnnualCalculateIncrementalReturn());
            annualCalculateStoreReturn = annualCalculateStoreReturn.add(yearData.getAnnualCalculateStoreReturn());
            annualAdjustIncrementalReturn = annualAdjustIncrementalReturn.add(yearData.getAnnualAdjustIncrementalReturn());
            annualAdjustStoreReturn = annualAdjustStoreReturn.add(yearData.getAnnualAdjustStoreReturn());
            yearSignNotReturn = yearSignNotReturn.add(yearData.getYearSignNotReturn());
            annualTotalIncrementalReturn = annualTotalIncrementalReturn.add(yearData.getAnnualTotalIncrementalReturn());
            annualTotalStoreReturn = annualTotalStoreReturn.add(yearData.getAnnualTotalStoreReturn());
            annualTotalReturnTotal = annualTotalReturnTotal.add(yearData.getAnnualTotalReturnTotal());
            yearEarlyReceivable = yearEarlyReceivable.add(yearData.getYearEarlyReceivable());
            yearEarlyRefund = yearEarlyRefund.add(yearData.getYearEarlyRefund());
            annualTotalYearEndReceivable = annualTotalYearEndReceivable.add(yearData.getAnnualTotalYearEndReceivable());
        }

        ReturnPlanDetailYearData detailYearData = new ReturnPlanDetailYearData();

        detailYearData.setYearSign(yearSign);
        detailYearData.setAnnualActualIncrementalReturn(annualActualIncrementalReturn);
        detailYearData.setAnnualActualStoreReturn(annualActualStoreReturn);
        detailYearData.setAnnualCalculateIncrementalReturn(annualCalculateIncrementalReturn);
        detailYearData.setAnnualCalculateStoreReturn(annualCalculateStoreReturn);
        detailYearData.setAnnualAdjustIncrementalReturn(annualAdjustIncrementalReturn);
        detailYearData.setAnnualAdjustStoreReturn(annualAdjustStoreReturn);
        detailYearData.setYearSignNotReturn(yearSignNotReturn);
        detailYearData.setAnnualTotalIncrementalReturn(annualTotalIncrementalReturn);
        detailYearData.setAnnualTotalStoreReturn(annualTotalStoreReturn);
        detailYearData.setAnnualTotalReturnTotal(annualTotalReturnTotal);
        detailYearData.setYearEarlyReceivable(yearEarlyReceivable);
        detailYearData.setYearEarlyRefund(yearEarlyRefund);
        detailYearData.setAnnualTotalYearEndReceivable(annualTotalYearEndReceivable);


        return detailYearData;
    }

    /**
     * 汇总计算月度数据列表
     * @param voList
     * @return
     */
    private List<ReturnPlanDetailMonthData> summaryMonthDataList(List<ReturnPlanYearDataVo> voList) {
        List<ReturnPlanDetailMonthData> monthDataList = new ArrayList<>();
        //1月期初应收
        BigDecimal beginningReceivable = BigDecimal.ZERO;

        for (int i = 1; i <=12 ; i++) {
            //获取指定月份月度数据列表
            List<ReturnPlanDetailMonthData> monthDatas = new ArrayList<>();
           for (ReturnPlanYearDataVo yearDataVo : voList){
               int month = i;
               List<ReturnPlanDetailMonthData> monthData = yearDataVo.getMonthDataList()
                       .stream().filter(s -> s.getMonth() == month).collect(Collectors.toList());
               if(CollectionUtils.isNotEmpty(monthData)){
                   monthDatas.addAll(monthData);
               }
           }
           if (CollectionUtils.isEmpty(monthDatas)){
               continue;
           }
           //计算1月期初应收
           if(1 == i){
               beginningReceivable = monthDatas.stream().map(ReturnPlanDetailMonthData::getMonthEarlyReceivable).reduce(BigDecimal.ZERO, BigDecimal::add);
           }

           ReturnPlanDetailMonthData monthData = new ReturnPlanDetailMonthData();

           //月初应收账款
           BigDecimal monthEarlyReceivable = BigDecimal.ZERO;
           //月度签约
           BigDecimal monthSign = BigDecimal.ZERO;
           //月度实际数-增量回款
           BigDecimal monthlyActualIncrementalReturn = BigDecimal.ZERO;
           //月度实际数-存量回款
           BigDecimal monthlyActualStoreReturn = BigDecimal.ZERO;
           //月度测算数-增量回款
           BigDecimal monthlyCalculateIncrementalReturn = BigDecimal.ZERO;
           //月度测算数-存量回款
           BigDecimal monthlyCalculateStoreReturn = BigDecimal.ZERO;
           //月度调整数-增量回款
           BigDecimal monthlyAdjustIncrementalReturn = BigDecimal.ZERO;
           //月度调整数-存量回款
           BigDecimal monthlyAdjustStoreReturn = BigDecimal.ZERO;
           //月度已签未回回款数
           BigDecimal monthSignNotReturn = BigDecimal.ZERO;
           //期初应退回款数
           BigDecimal refundable = BigDecimal.ZERO;
           //月度回款合计-增量回款
           BigDecimal monthlyTotalIncrementalReturn = BigDecimal.ZERO;
           //月度回款合计-存量回款
           BigDecimal monthlyTotalStoreReturn = BigDecimal.ZERO;
           //月度回款合计-回款合计
           BigDecimal monthlyTotalReturnTotal = BigDecimal.ZERO;
           //月度回款合计-月末应收账款
           BigDecimal monthlyTotalMonthEndReceivable = BigDecimal.ZERO;
           //累计年度签约合计
           BigDecimal yearSignAmountTotal = BigDecimal.ZERO;
           //累计签约合计
           BigDecimal signAmountTotal = BigDecimal.ZERO;
           //累计年度回款合计
           BigDecimal yearReturnAmountTotal = BigDecimal.ZERO;
           //累计回款合计
           BigDecimal returnAmountTotal = BigDecimal.ZERO;

           for(ReturnPlanDetailMonthData returnPlanDetailMonthData : monthDatas){
               monthEarlyReceivable = monthEarlyReceivable.add(returnPlanDetailMonthData.getMonthEarlyReceivable());
               monthSign = monthSign.add(returnPlanDetailMonthData.getMonthSign());
               monthlyActualIncrementalReturn = monthlyActualIncrementalReturn.add(returnPlanDetailMonthData.getMonthlyActualIncrementalReturn());
               monthlyActualStoreReturn = monthlyActualStoreReturn.add(returnPlanDetailMonthData.getMonthlyActualStoreReturn());
               monthlyCalculateIncrementalReturn = monthlyCalculateIncrementalReturn.add(returnPlanDetailMonthData.getMonthlyCalculateIncrementalReturn());
               monthlyCalculateStoreReturn = monthlyCalculateStoreReturn.add(returnPlanDetailMonthData.getMonthlyCalculateStoreReturn());
               monthlyAdjustIncrementalReturn = monthlyAdjustIncrementalReturn.add(returnPlanDetailMonthData.getMonthlyAdjustIncrementalReturn());
               monthlyAdjustStoreReturn = monthlyAdjustStoreReturn.add(returnPlanDetailMonthData.getMonthlyAdjustStoreReturn());
               monthSignNotReturn = monthSignNotReturn.add(returnPlanDetailMonthData.getMonthSignNotReturn());
               refundable = refundable.add(returnPlanDetailMonthData.getRefundable());
               monthlyTotalIncrementalReturn = monthlyTotalIncrementalReturn.add(returnPlanDetailMonthData.getMonthlyTotalIncrementalReturn());
               monthlyTotalStoreReturn = monthlyTotalStoreReturn.add(returnPlanDetailMonthData.getMonthlyTotalStoreReturn());
               monthlyTotalReturnTotal = monthlyTotalReturnTotal.add(returnPlanDetailMonthData.getMonthlyTotalReturnTotal());
               monthlyTotalMonthEndReceivable = monthlyTotalMonthEndReceivable.add(returnPlanDetailMonthData.getMonthlyTotalMonthEndReceivable());
               yearSignAmountTotal = yearSignAmountTotal.add(returnPlanDetailMonthData.getYearSignAmountTotal());
               signAmountTotal = signAmountTotal.add(returnPlanDetailMonthData.getSignAmountTotal());
               yearReturnAmountTotal = yearReturnAmountTotal.add(returnPlanDetailMonthData.getYearReturnAmountTotal());
               returnAmountTotal = returnAmountTotal.add(returnPlanDetailMonthData.getReturnAmountTotal());
           }
            //月份
            monthData.setMonth(i);
            //月初应收账款
            monthData.setMonthEarlyReceivable(monthEarlyReceivable);
            //月度签约
            monthData.setMonthSign(monthSign);
            //月度实际数-增量回款
            monthData.setMonthlyActualIncrementalReturn(monthlyActualIncrementalReturn);
            //月度实际数-存量回款
            monthData.setMonthlyActualStoreReturn(monthlyActualStoreReturn);
            //月度测算数-增量回款
            monthData.setMonthlyCalculateIncrementalReturn(monthlyCalculateIncrementalReturn);
            //月度测算数-存量回款
            monthData.setMonthlyCalculateStoreReturn(monthlyCalculateStoreReturn);
            //月度调整数-增量回款
            monthData.setMonthlyAdjustIncrementalReturn(monthlyAdjustIncrementalReturn);
            //月度调整数-存量回款
            monthData.setMonthlyAdjustStoreReturn(monthlyAdjustStoreReturn);
            //月度已签未回回款数
            monthData.setMonthSignNotReturn(monthSignNotReturn);
            //期初应退回款数
            monthData.setRefundable(refundable);
            //月度回款合计-增量回款
            monthData.setMonthlyTotalIncrementalReturn(monthlyTotalIncrementalReturn);
            //月度回款合计-存量回款
            monthData.setMonthlyTotalStoreReturn(monthlyTotalStoreReturn);
            //月度回款合计-回款合计
            monthData.setMonthlyTotalReturnTotal(monthlyTotalReturnTotal);
            //月度回款合计-月末应收账款
            monthData.setMonthlyTotalMonthEndReceivable(monthlyTotalMonthEndReceivable);
            //累计年度签约合计
            monthData.setYearSignAmountTotal(yearSignAmountTotal);
            //累计签约合计
            monthData.setSignAmountTotal(signAmountTotal);
            //累计年度回款合计
            monthData.setYearReturnAmountTotal(yearReturnAmountTotal);
            //累计回款合计
            monthData.setReturnAmountTotal(returnAmountTotal);
            //累计签约回款率(开售月累计到当月回款合计/开售月累计到累计当月签约金额)
            BigDecimal signReturnRate = BigDecimalUtils.divBigDecimal(returnAmountTotal,signAmountTotal,5);
            //年度综合回款率(从当年1月累计到当月回款合计/(当年1月的期初应收 + 从当年1月累计到累计当月签约金额)
            BigDecimal annualOverallReturnRate = BigDecimalUtils
                    .divBigDecimal(yearReturnAmountTotal,beginningReceivable.add(yearSignAmountTotal),5);
            //累计签约回款率
            monthData.setSignReturnRate(signReturnRate);
            //年度综合回款率
            monthData.setAnnualOverallReturnRate(annualOverallReturnRate);
            monthDataList.add(monthData);
        }
        return monthDataList;
    }

    /**
     *  组合业态数据数据
     * @param signPlanVersionId 签约计划版本id
     * @return
     */
    @Override
    public CombinationProductDataBO getCombinationProductData(String signPlanVersionId) {
        CombinationProductDataBO combinationProductData = new CombinationProductDataBO();

        //获取各组团下各组合业态向下结转日期列表
        List<CombinationProductDataDto> combinationProductDataList = signPlanVersionSupplyDetailMapper.selectCarryOverTime(signPlanVersionId,OrgTypeEunm.ORG_PRODUCT.getKey(), IsDeleteEnum.NO.getKey());

        combinationProductData.setCombinationProductDataList(combinationProductDataList);
        combinationProductData.initializeMap();
        return combinationProductData;
    }

    /**
     * 查询回款计划编辑页面数据
     * @param versionId 回款计划版本id
     * @param groupId 组团id
     * @return
     */
    @Override
    public ReturnPlanEditDataVo getEditDataList(String versionId, String groupId) {
        ReturnPlanVersion returnPlanVersion = returnPlanVersionMapper.selectById(versionId);
        if(null == returnPlanVersion || returnPlanVersion.getIsDelete() == IsDeleteEnum.YES.getKey()){
            throw new MsgException(CodeEnum.NOT_RETURN_PLAN_VERSION_ERROR);
        }
        //获取组合业态数据
        CombinationProductDataBO combinationProductData = getCombinationProductData(returnPlanVersion.getSignPlanVersionId());

        //获取回款计划详情列表
        List<ReturnPlanVersionDetail> detailList = returnPlanVersionDetailService.getListByVersionIdAndgroupId(versionId,groupId);
        if(CollectionUtils.isEmpty(detailList)){
            throw new MsgException(CodeEnum.NOT_RETURN_PLAN_VERSION_DETAIL_ERROR);
        }
        //获取最早回款时间
        LocalDateTime earlyDateTime = detailList.get(0).getReturnTime();
        //获取最晚回款时间
        LocalDateTime lastDateTime = detailList.get(detailList.size() - 1).getReturnTime();
        lastDateTime = LocalDateTime.of(lastDateTime.getYear(), 12, 1, 0, 0);

        //对各租户业态分组（key: 组合业态编码,value:回款计划详情列表）
        Map<String, List<ReturnPlanVersionDetail>> detailMap = detailList.stream()
                .collect(Collectors.groupingBy(ReturnPlanVersionDetail::getCombinationProductCode));

        List<ReturnPlanDetailDataVo> voList = new ArrayList<>();
        for(Map.Entry<String, List<ReturnPlanVersionDetail>> entryMap :detailMap.entrySet()){
            String combinationProductCode = entryMap.getKey();

            ReturnPlanDetailDataVo vo = new ReturnPlanDetailDataVo();

            List<ReturnPlanVersionDetail> details = entryMap.getValue();
            ReturnPlanVersionDetail detail = details.get(0);
            vo.setCombinationProductCode(entryMap.getKey());
            vo.setProductName(detail.getProductName());
            vo.setBusinessTypeName(detail.getBusinessTypeName());
            vo.setFreeTypeName(detail.getFreeTypeName());
            vo.setProTypeName(detail.getProTypeName());
            //获取总货值
            BigDecimal totalValue = combinationProductData.getTotalValue(groupId,combinationProductCode);
            vo.setTotalValue(totalValue);

            List<ReturnPlanVersionDetail> returnPlanVersionDetailList = new ArrayList<>();
            Map<String, ReturnPlanVersionDetail> detailByYearMonthMap = details.stream()
                    .collect(Collectors.toMap(s -> LocalDateTimeUtils.formatTime(s.getReturnTime(), DateUtils.DATE_JFP_STR), s -> s));

            for (LocalDateTime dateTime = earlyDateTime; LocalDateTimeUtils.compareDate(dateTime,lastDateTime,1); dateTime = dateTime.plusMonths(1)) {
                String yearMonth = LocalDateTimeUtils.formatTime(dateTime, DateUtils.DATE_JFP_STR);

                ReturnPlanVersionDetail returnPlanVersionDetail = detailByYearMonthMap.get(yearMonth);
                if(null == returnPlanVersionDetail){
                    returnPlanVersionDetail = new ReturnPlanVersionDetail();
                    BeanUtils.copyProperties(detail,returnPlanVersionDetail);
                    returnPlanVersionDetail.setId(UUIDUtils.create());
                    returnPlanVersionDetail.setIsAllowedDelete(WhetherEnum.YES.getKey());
                    returnPlanVersionDetail.initialize(dateTime,detail.getCreateBy());
                }
                returnPlanVersionDetailList.add(returnPlanVersionDetail);
            }
            vo.setDetailList(returnPlanVersionDetailList);
            voList.add(vo);
        }

        ReturnPlanEditDataVo returnPlanEditDataVO = new ReturnPlanEditDataVo();
        returnPlanEditDataVO.setVersionId(versionId);
        returnPlanEditDataVO.setDataSource(returnPlanVersion.getDataSource());
        returnPlanEditDataVO.setGroupId(groupId);
        returnPlanEditDataVO.setAffiliationMonth(returnPlanVersion.getAffiliationMonth());
        returnPlanEditDataVO.setEarliestYearMonth(LocalDateTimeUtils.formatTime(earlyDateTime, DateUtils.DATE_JFP_STR));
        returnPlanEditDataVO.setLatestYearMonth(LocalDateTimeUtils.formatTime(lastDateTime, DateUtils.DATE_JFP_STR));
        returnPlanEditDataVO.setVoList(voList);
        return returnPlanEditDataVO;
    }

    /**
     * 删除回款计划
     * @param versionId
     * @param user
     */
    @Transactional
    @Override
    public void delById(String versionId, IdmUser user) {
        if(StringUtils.isBlank(versionId) || null == user){
            throw new MsgException(CodeEnum.REQUEST_PARAM_NOT_FULL_ERROR);
        }
        ReturnPlanVersion returnPlanVersion = returnPlanVersionMapper.selectById(versionId);
        if(null == returnPlanVersion || returnPlanVersion.getIsDelete() == IsDeleteEnum.YES.getKey()){
            throw new MsgException(CodeEnum.NOT_RETURN_PLAN_VERSION_ERROR);
        }

        List<String> versionStateList = Arrays.asList(VersionStatusEnum.SUCCESSAPPROVED.getKey(), VersionStatusEnum.APPROVED.getKey(),
                VersionStatusEnum.EXAMINEDAPPROVED.getKey(), VersionStatusEnum.FAILUREAPPROVED.getKey());
        String versionState = returnPlanVersion.getVersionState();
        if(versionStateList.contains(versionState)){
            throw new MsgException(CodeEnum.RETURN_DEL_ERROR);
        }

        returnPlanVersionMapper.delById(versionId,user.getAlias(),IsDeleteEnum.YES.getKey());
        returnPlanVersionDetailMapper.delByVersionId(versionId,user.getAlias(),IsDeleteEnum.YES.getKey());
        returnPlanVersionCoefficientMapper.delByReturnPlanVersionId(versionId,user.getAlias(),IsDeleteEnum.YES.getKey());

        saveVersionLog(Arrays.asList(versionId),VersionEventEnum.MANUAL_DELETE.getKey(),versionState,user.getAlias(),"删除回款版本","删除回款版本");

    }

    /**
     * 更新版本状态
     * @param versionIds
     * @param versionState
     * @param user
     */
    @Override
    public void updateState(List<String> versionIds,String versionEvent,String versionState, IdmUser user) {
        //参数校验
        if(CollectionUtils.isEmpty(versionIds) || StringUtils.isBlank(versionState) || null == user){
            throw new MsgException(CodeEnum.REQUEST_PARAM_NOT_FULL_ERROR);
        }

        List<ReturnPlanVersion> returnPlanVersionList = returnPlanVersionWrapper.selectList(versionIds, IsDeleteEnum.NO.getKey());
        if(CollectionUtils.isEmpty(returnPlanVersionList)){
            return;
        }

        //变更前的状态
        String beforeState = returnPlanVersionList.get(0).getVersionState();
        returnPlanVersionList.forEach(s->{
            s.setVersionState(versionState);
            s.setUpdateBy(user.getAlias());
            s.setUpdateTime(LocalDateTime.now());
        });
        this.updateBatchById(returnPlanVersionList);

        //审批通过后,更新项目下分期最新版本标识
        if(VersionStatusEnum.SUCCESSAPPROVED.getKey().equals(versionState)){
        	//自动复制预算版
        	autoCopyReturnBudgetVersion(returnPlanVersionList);
        	
            updateStageNewestByProjectId(returnPlanVersionList.get(0).getProjectId());
        }
        saveVersionLog(versionIds,versionEvent,beforeState,user.getAlias(),"接OA审批消息",null);
    }
    
    private boolean isAlowCopyBudget(String baseVersionIdentify,String rVersionIdentify,String sVersionIdentify) {
    	if(StrUtils.isNotEmpty(rVersionIdentify) && StrUtils.isNotEmpty(sVersionIdentify) && rVersionIdentify.equals(sVersionIdentify) && baseVersionIdentify.equals(sVersionIdentify)) {
    		return true;
    	}
    	return false;
    }
    
    private void autoCopyReturnBudgetVersion(List<ReturnPlanVersion> returnPlanVersionList) {
    	boolean flag = false;
    	String baseVersionIdentify = budgetVersionIdentifyService.getVersion();
    	for(ReturnPlanVersion returnObj : returnPlanVersionList) {
    		String versionIdentify = returnObj.getVersionIdentify();
    		String signPlanVersionId = returnObj.getSignPlanVersionId();
    		SignPlanVersion signPlanVersion = signPlanVersionMapper.selectByIdAndDel(signPlanVersionId, IsDeleteEnum.NO.getKey());
    		if(isAlowCopyBudget(baseVersionIdentify,versionIdentify,signPlanVersion.getVersionIdentify())) {
    			ReturnPlanVersion returnPlanVersion = new ReturnPlanVersion();
    	        String id = UUIDUtils.create();
    	        //版本号
    	        String versionNum = getVersionNum(returnObj.getStageId(), VersionTypeEunm.BUDGET.getKey(), versionIdentify,returnObj.getAffiliationMonth());

    	        String affiliationMonth = returnObj.getAffiliationMonth();
    	        if(StrUtils.isEmpty(affiliationMonth)) {
    	        	affiliationMonth = LocalDateTimeUtils.formatTime(returnObj.getCreateTime(),"yyyy-MM");
    	        }
    	        
    	        BeanUtils.copyProperties(returnObj, returnPlanVersion);
    	        
    	        returnPlanVersion.setId(id);
    	        returnPlanVersion.setVersionType(VersionTypeEunm.BUDGET.getKey());
    	        returnPlanVersion.setVersionNum(versionNum);
    	        returnPlanVersion.setVersionState(VersionStatusEnum.SYSGENERATION.getKey());
    	        returnPlanVersion.setCompileYear(versionIdentify.substring(0, 4));
    	        returnPlanVersion.setVersionIdentify(versionIdentify);
    	        returnPlanVersion.setAffiliationMonth(affiliationMonth);
    	        returnPlanVersion.setSourceVersionId(returnObj.getId());
    	        returnPlanVersion.setIsAssessment(WhetherEnum.NO.getKey());
    	        returnPlanVersion.setIsFinalization(WhetherEnum.NO.getKey());
    	        returnPlanVersion.setCreateBy(Constants.SYS);
    	        returnPlanVersion.setCreateTime(LocalDateTime.now());
    	        returnPlanVersion.setUpdateBy(null);
    	        returnPlanVersion.setUpdateTime(null);
    	        returnPlanVersion.setVersionCreate(VersionCreateEnum.AUTOMATICCOPY.getKey());
    	        returnPlanVersion.setIsDelete(IsDeleteEnum.NO.getKey());
    	        returnPlanVersion.setDescription("系统自动复制预算版");
    	        returnPlanVersion.setIsStageNewest(WhetherEnum.NO.getKey());
    	        
    	        returnPlanVersionMapper.insert(returnPlanVersion);

    	        returnPlanVersionDetailMapper.copyVersionDetail(returnObj.getId(),id);
    	        
    	        returnPlanVersionCoefficientMapper.copyVersionCoefficient(returnObj.getId(),id);
    	        
    	        saveVersionLog(Arrays.asList(id),VersionEventEnum.SYS_GENERATE.getKey(),"",Constants.SYS,"新建回款计划",null);
    	        flag = true;
    		}
    	}
		
    	if(flag) {
    		//下发通知预算系统取数
    		signPlanVersionDetailService.sendInformFetch(returnPlanVersionList.get(0).getProjectId(),2);
    	}
	}

	@Override
	public void copyToBudgetVersion(String versionId, String versionIdentify,IdmUser user) {
		ReturnPlanVersion returnPlanVersion = returnPlanVersionMapper.selectById(versionId);
        if(returnPlanVersion != null) {
        	//1、项目锁定不允许复制
        	Integer lockStatus = budgetLockRecordService.getLockStatus(returnPlanVersion.getProjectId(),"return");
			if(lockStatus != null && lockStatus.intValue() == WhetherEnum.YES.getKey()) {
				throw new MsgException(CodeEnum.VERSION_IS_LOCK);
			}
			//2、分期已存在定稿版不允许复制
			boolean isExistFinalVersion = budgetLockRecordService.isExistFinalVersion(returnPlanVersion.getStageId(),"return",versionIdentify);
			if(isExistFinalVersion) {
				throw new MsgException(CodeEnum.SIGN_VERSION_EXIST_FINAL);
			}
			//3、分期的经营方式全部都是可租或不可租售的分期，不允许创建版本
        	if (!getStageIsSaly(returnPlanVersion.getSupplyPlanVersionId())) {
  	          throw new MsgException(CodeEnum.CREATE_NOT_BUILD_SALY_TYPE_ERROR);
        	}
        	//4、分期禁用不允许复制
	  		MdmStage mdmStage = stageMapper.selectByFId(returnPlanVersion.getStageId(), IsDeleteEnum.NO.getKey());
	  		if(mdmStage != null && StrUtils.isNotEmpty(mdmStage.getStatus()) && mdmStage.getStatus().equals(StageStatusEnum.INACTIVE.getKey())) {
	  			 throw new MsgException(CodeEnum.INACTIVE_STAGE_NOT_BUDGET);
	  		}
	  	    //5、签约计划不存在同版本标签预算版不允许复制
	  		SignPlanVersion signPlanVersion = signPlanVersionMapper.selectByIdAndDel(returnPlanVersion.getSignPlanVersionId(), IsDeleteEnum.NO.getKey());
	  		if(StrUtils.isNotEmpty(versionIdentify) &&  signPlanVersion != null && !versionIdentify.equals(signPlanVersion.getVersionIdentify())) {
	  			throw new MsgException(CodeEnum.NOT_SIGN_BUDGET_VERSION);
	  		}
	  		//6、状态不是审批通过或系统生产不允许复制
        	String state = returnPlanVersion.getVersionState();
        	if(state.equals(VersionStatusEnum.SUCCESSAPPROVED.getKey()) || state.equals(VersionStatusEnum.SYSGENERATION.getKey())) {
        		
        		copyVersionToBudgetVersion(returnPlanVersion,versionIdentify,user);
                
        		updateStageNewestByProjectId(returnPlanVersion.getProjectId());
        		
        		//下发通知预算系统取数
        		signPlanVersionDetailService.sendInformFetch(returnPlanVersion.getProjectId(),1);
        	}else {
        		 throw new MsgException(CodeEnum.NOT_VALID_VERSION);
        	}
        }
	}

	private void copyVersionToBudgetVersion(ReturnPlanVersion sourceReturnPlanVersion, String versionIdentify, IdmUser user) {
		ReturnPlanVersion returnPlanVersionNew = new ReturnPlanVersion();
        String id = UUIDUtils.create();
        //版本归属年月
        String affiliationMonth = sourceReturnPlanVersion.getAffiliationMonth();
        if(StrUtils.isEmpty(affiliationMonth)) {
        	affiliationMonth = LocalDateTimeUtils.formatTime(sourceReturnPlanVersion.getCreateTime(),"yyyy-MM");
        }
        
        BeanUtils.copyProperties(sourceReturnPlanVersion, returnPlanVersionNew);
        //版本号
        String versionNum = getVersionNum(sourceReturnPlanVersion.getStageId(), VersionTypeEunm.BUDGET.getKey(), versionIdentify,affiliationMonth);
        //当前分期下无任何预算版，直接复制为定稿版
        if(StrUtils.isEmpty(versionIdentify)) {
        	List<ReturnPlanVersion> list = returnPlanVersionMapper.selectListByStageIdAndVersionType(sourceReturnPlanVersion.getStageId(),VersionTypeEunm.BUDGET.getKey(),IsDeleteEnum.NO.getKey());
        	if(CollectionUtils.isEmpty(list)) {
        		versionIdentify = LocalDateTime.now().getYear()+"年度预算定稿版";
        	}else {
        		throw new MsgException(CodeEnum.NOT_SET_BUDGET);
        	}
        }
        
        returnPlanVersionNew.setId(id);
        returnPlanVersionNew.setVersionType(VersionTypeEunm.BUDGET.getKey());
        returnPlanVersionNew.setVersionNum(versionNum);
        returnPlanVersionNew.setVersionState(VersionStatusEnum.SYSGENERATION.getKey());
        returnPlanVersionNew.setSourceVersionId(sourceReturnPlanVersion.getId());
        returnPlanVersionNew.setVersionIdentify(versionIdentify);
        returnPlanVersionNew.setIsAssessment(WhetherEnum.NO.getKey());
        returnPlanVersionNew.setAffiliationMonth(affiliationMonth);
        returnPlanVersionNew.setIsFinalization(WhetherEnum.NO.getKey());
        returnPlanVersionNew.setCreateBy(user.getAlias());
        returnPlanVersionNew.setCreateTime(LocalDateTime.now());
        returnPlanVersionNew.setUpdateBy(null);
        returnPlanVersionNew.setUpdateTime(null);
        returnPlanVersionNew.setVersionCreate(VersionCreateEnum.MANUALCOPY.getKey());
        returnPlanVersionNew.setIsDelete(IsDeleteEnum.NO.getKey());
        returnPlanVersionNew.setDescription("手动设置版本到预算版");
        returnPlanVersionNew.setIsStageNewest(WhetherEnum.NO.getKey());
        
        returnPlanVersionMapper.insert(returnPlanVersionNew);

        returnPlanVersionDetailMapper.copyVersionDetail(sourceReturnPlanVersion.getId(),id);
        
        returnPlanVersionCoefficientMapper.copyVersionCoefficient(sourceReturnPlanVersion.getId(),id);
        
        saveVersionLog(Arrays.asList(id),VersionEventEnum.SYS_GENERATE.getKey(),"",user.getAlias(),"新建回款计划",null);
	}
	
	
	private boolean getStageIsSaly(String supplyPlanVersionId) {
		 List<String> salyTypeList = Arrays.asList(SalyTypeEnum.SALABLE.getKey(), SalyTypeEnum.STAGE_SELF_SUSTAINMENT.getKey());
		 int count = supplyPlanVersionDetailMapper.selectSupplyIsSalableOrStageSelfSustainment(supplyPlanVersionId,salyTypeList);
		 if(count > 0) {
			 return true;
		 }
		 return false;
	}

	@Override
	public void copyBudgetVersion(String versionId,String versionIdentify, IdmUser user) {
		ReturnPlanVersion returnPlanVersion = returnPlanVersionMapper.selectById(versionId);
        if(returnPlanVersion != null) {
        	//1、非预算季不允许复制
	  		if(StrUtils.isEmpty(versionIdentify)) {
	  			throw new MsgException(CodeEnum.NOT_BUDGET);
	  		}
        	//2、项目锁定不允许复制
        	Integer lockStatus = budgetLockRecordService.getLockStatus(returnPlanVersion.getProjectId(),"return");
			if(lockStatus != null && lockStatus.intValue() == WhetherEnum.YES.getKey()) {
				throw new MsgException(CodeEnum.VERSION_IS_LOCK);
			}
			//3、分期已存在定稿版不允许复制
			boolean isExistFinalVersion = budgetLockRecordService.isExistFinalVersion(returnPlanVersion.getStageId(),"return",versionIdentify);
			if(isExistFinalVersion) {
				throw new MsgException(CodeEnum.SIGN_VERSION_EXIST_FINAL_NOT);
			}
			//4、分期的经营方式全部都是可租或不可租售的分期，不允许创建版本
        	if (!getStageIsSaly(returnPlanVersion.getSupplyPlanVersionId())) {
  	          throw new MsgException(CodeEnum.CREATE_NOT_BUILD_SALY_TYPE_ERROR);
        	}
        	//5、分期禁用不允许复制
	  		MdmStage mdmStage = stageMapper.selectByFId(returnPlanVersion.getStageId(), IsDeleteEnum.NO.getKey());
	  		if(mdmStage != null && StrUtils.isNotEmpty(mdmStage.getStatus()) && mdmStage.getStatus().equals(StageStatusEnum.INACTIVE.getKey())) {
	  			 throw new MsgException(CodeEnum.INACTIVE_STAGE_NOT_COPY_BUDGET);
	  		}
	  		//6、签约计划不存在同版本标签预算版不允许复制
	  		SignPlanVersion signPlanVersion = signPlanVersionMapper.selectByIdAndDel(returnPlanVersion.getSignPlanVersionId(), IsDeleteEnum.NO.getKey());
	  		if(StrUtils.isNotEmpty(versionIdentify) &&  signPlanVersion != null && !versionIdentify.equals(signPlanVersion.getVersionIdentify())) {
	  			throw new MsgException(CodeEnum.NOT_SIGN_BUDGET_VERSION);
	  		}
	  	   
	  		copyReturnPlanBudgetVersion(returnPlanVersion,versionIdentify,user);
            
    		updateStageNewestByProjectId(returnPlanVersion.getProjectId());
        }
		
	}
	
	private void copyReturnPlanBudgetVersion(ReturnPlanVersion sourceReturnPlanVersion,String versionIdentify,IdmUser user) {
		ReturnPlanVersion returnPlanVersionNew = new ReturnPlanVersion();
        String id = UUIDUtils.create();
        String affiliationMonth = sourceReturnPlanVersion.getAffiliationMonth();
        if(StrUtils.isEmpty(affiliationMonth)) {
        	affiliationMonth = LocalDateTimeUtils.formatTime(sourceReturnPlanVersion.getCreateTime(),"yyyy-MM");
        }
        BeanUtils.copyProperties(sourceReturnPlanVersion, returnPlanVersionNew);
        //版本号
        String versionNum = getVersionNum(sourceReturnPlanVersion.getStageId(), sourceReturnPlanVersion.getVersionType(), 
        		sourceReturnPlanVersion.getVersionIdentify(),affiliationMonth);
        
        returnPlanVersionNew.setId(id);
        returnPlanVersionNew.setVersionNum(versionNum);
        returnPlanVersionNew.setVersionState(VersionStatusEnum.SYSGENERATION.getKey());
        returnPlanVersionNew.setSourceVersionId(sourceReturnPlanVersion.getId());
        returnPlanVersionNew.setAffiliationMonth(affiliationMonth);
        returnPlanVersionNew.setVersionIdentify(versionIdentify);
        returnPlanVersionNew.setCompileYear(versionIdentify.substring(0, 4));
        returnPlanVersionNew.setIsFinalization(WhetherEnum.NO.getKey());
        returnPlanVersionNew.setCreateBy(user.getAlias());
        returnPlanVersionNew.setCreateTime(LocalDateTime.now());
        returnPlanVersionNew.setUpdateBy(null);
        returnPlanVersionNew.setUpdateTime(null);
        returnPlanVersionNew.setVersionCreate(VersionCreateEnum.MANUALCOPY.getKey());
        returnPlanVersionNew.setIsDelete(IsDeleteEnum.NO.getKey());
        returnPlanVersionNew.setDescription("手动复制预算版");
        returnPlanVersionNew.setIsStageNewest(WhetherEnum.NO.getKey());
        
        returnPlanVersionMapper.insert(returnPlanVersionNew);

        returnPlanVersionDetailMapper.copyVersionDetail(sourceReturnPlanVersion.getId(),id);
        
        returnPlanVersionCoefficientMapper.copyVersionCoefficient(sourceReturnPlanVersion.getId(),id);
        
        saveVersionLog(Arrays.asList(id),VersionEventEnum.SYS_GENERATE.getKey(),"",user.getAlias(),"新建回款计划",null);
        
        //下发通知预算系统取数
        signPlanVersionDetailService.sendInformFetch(sourceReturnPlanVersion.getProjectId(),2);
	}

	/**
     * 回款计划编辑页面保存
     * @param paramList  保存参数
     * @param user 用户
     */
    @Transactional
    @Override
    public void editSave(List<ReturnPlanDetailSaveParam> paramList, IdmUser user) {
        //参数校验
        if(CollectionUtils.isEmpty(paramList) || null == user){
            throw new MsgException(CodeEnum.REQUEST_PARAM_NOT_FULL_ERROR);
        }

        //获取回款计划版本数据
        String versionId = paramList.get(0).getDetailList().get(0).getVersionId();
        ReturnPlanVersion returnPlanVersion = returnPlanVersionMapper.selectById(versionId);
        if(null == returnPlanVersion || returnPlanVersion.getIsDelete() == IsDeleteEnum.YES.getKey()){
            throw new MsgException(CodeEnum.NOT_RETURN_PLAN_VERSION_ERROR);
        }

        //判断是否已经审批中、审批完成
        List<String> versionStatusList = Arrays.asList(VersionStatusEnum.APPROVED.getKey(), VersionStatusEnum.SUCCESSAPPROVED.getKey());
        if(versionStatusList.contains(returnPlanVersion.getVersionState())){
            throw new MsgException(CodeEnum.RETURN_SAVE_EXIST_VERSION_ERROR);
        }

        String groupId = paramList.get(0).getDetailList().get(0).getGroupId();
        //逻辑删除指定组团数据
        returnPlanVersionDetailService.delByGroupId(versionId,groupId,user.getAlias());

        for(ReturnPlanDetailSaveParam param : paramList){
            List<ReturnPlanVersionDetail> detailList = param.getDetailList();
            if(CollectionUtils.isEmpty(detailList)){
                throw new MsgException(CodeEnum.REQUEST_PARAM_NOT_FULL_ERROR);
            }
            //编辑保存
            returnPlanVersionDetailService.editSave(detailList);
        }

        String beforeState = returnPlanVersion.getVersionState();

        returnPlanVersion.setUpdateBy(user.getAlias());
        returnPlanVersion.setUpdateTime(LocalDateTime.now());
        // 编制中、待提交审批、审批已驳回状态的版本编辑后不修改版本状态
        if (returnPlanVersion.getVersionState().equals(VersionStatusEnum.NOTPREPARED.getKey())) {
            String afterState = VersionStatusEnum.ORGANIZATION.getKey();
            returnPlanVersion.setVersionState(afterState);
        }
        returnPlanVersionMapper.updateById(returnPlanVersion);
        saveVersionLog(Arrays.asList(versionId),VersionEventEnum.UPDATE_SAVE.getKey(),beforeState,user.getAlias(),"保存回款计划",null);

        //待提交审批、审批已驳回状态的版本保存后更新审批快照表
        List<String> versionStatuss = Arrays.asList(VersionStatusEnum.EXAMINEDAPPROVED.getKey(), VersionStatusEnum.FAILUREAPPROVED.getKey());
        if(versionStatuss.contains(returnPlanVersion.getVersionState())){
            ReturnPlanApprovalPreviewVo approvalPreview = getReturnPlanApprovalPreviewVo(returnPlanVersion.getProjectId());
            approvalPreviewService.saveData(approvalPreview.getApprovalId(),JSON.toJSONString(approvalPreview),user);
        }
    }

    /**
     * 获取回款计划编辑页面组团列表
     * @param versionId 版本id
     * @return
     */
    @Override
    public List<ReturnPlanGroupVo> getGroupList(String versionId) {
        //参数校验
        if(StringUtils.isBlank(versionId)){
            throw new MsgException(CodeEnum.REQUEST_PARAM_NOT_FULL_ERROR);
        }
        ReturnPlanVersion returnPlanVersion = returnPlanVersionMapper.selectById(versionId);
        if(null == returnPlanVersion || returnPlanVersion.getIsDelete() == IsDeleteEnum.YES.getKey()){
            throw new MsgException(CodeEnum.NOT_RETURN_PLAN_VERSION_ERROR);
        }
        //获取回款计划编辑页面组团列表
        return returnPlanVersionDetailService.getGroupList(versionId);
    }

    /**
     * 获取回款计划回款系数
     * @param versionId
     * @return
     */
    @Override
    public List<ReturnPlanReturnCoefficientDetailVo> getReturnCoefficientDetailList(String versionId) {
        //参数校验
        if(StringUtils.isBlank(versionId)){
            throw new MsgException(CodeEnum.REQUEST_PARAM_NOT_FULL_ERROR);
        }
        ReturnPlanVersion returnPlanVersion = returnPlanVersionMapper.selectById(versionId);
        if(null == returnPlanVersion || returnPlanVersion.getIsDelete() == IsDeleteEnum.YES.getKey()){
            throw new MsgException(CodeEnum.NOT_RETURN_PLAN_VERSION_ERROR);
        }
        return returnPlanVersionCoefficientService.getReturnCoefficientDetailList(versionId);
    }

    @Autowired
    DataSource dataSource;

    /**
     * 发送回款代办
     * @param signId
     */
    @Transactional
    @Override
    public void returnNoticeDispose(String signId) {
        if(StringUtils.isBlank(signId)){
            throw new MsgException(CodeEnum.REQUEST_PARAM_NOT_FULL_ERROR);
        }
        //查询签约计划
        SignPlanVersion signPlanVersion = signPlanVersionMapper.selectByIdAndDel(signId, IsDeleteEnum.NO.getKey());
        if (null == signPlanVersion) {
            throw new MsgException(CodeEnum.NOT_SIGN_PLAN_VERSION);
        }
        String versionType = signPlanVersion.getVersionType();
        String stageId = signPlanVersion.getProjectfId();

        if(versionType.equals(VersionTypeEunm.BUDGET.getKey())){
            return;
        }

        //获取版本类型名称
        String versionTypeName = VersionTypeEunm.getValueByKey(signPlanVersion.getVersionType());
        //获取父级全路径组织数据
        FullPathOrgData fullPathOrgData = projectByStagesService.getParentFullPathOrgData(signPlanVersion.getProjectfId());
        //获取项目财务PM下的用户信息列表
        List<IdmUser> idmUserList = idmUserService.getPositionGroupUserList(Constants.PROJECT_FINANCE_PM, fullPathOrgData.getProjectId());

        //回款计划版本id
        ReturnPlanVersion returnPlanVersion = null;
        //是否允许自动创建回款计划版本
        boolean whether = whetherAutomaticCreate(stageId, versionType);
        if(whether){
            //手动开启事务
            DataSourceTransactionManager dSTransactionManager = new DataSourceTransactionManager();
            dSTransactionManager.setDataSource(dataSource);
            DefaultTransactionDefinition dtDefinition = new DefaultTransactionDefinition();
            //新发起一个事务
            dtDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
            //获取事务状态
            TransactionStatus transactionStatus = dSTransactionManager.getTransaction(dtDefinition);
            try {
                //自动创建回款计划
                returnPlanVersion = automaticallyCreate(stageId, versionType,signPlanVersion.getVersionIdentify());
                dSTransactionManager.commit(transactionStatus);
            }catch (MsgException e){
                dSTransactionManager.rollback(transactionStatus);
                log.error(e.getMsg());
                SysLogUtils.addSysLog(LogEventEnum.ADD,"回款计划","自动创建回款计划失败",e.getMsg());
                returnPlanVersion = null;
            }catch (Exception e){
                dSTransactionManager.rollback(transactionStatus);
                log.error(e.getMessage());
                SysLogUtils.addSysLog(LogEventEnum.ADD,"回款计划","自动创建回款计划失败",e.getMessage());
                returnPlanVersion = null;

            }
        }

        IdmUser handUser = new IdmUser();
        handUser.setAlias(Constants.SYS);
        handUser.setUsercn("系统");
        //查询是否已有代办，将原代办消除
        deleteTodoInfo(stageId + versionType,OperatorTypeEnum.DELETE.getKey(),handUser);

        //1、已创建,提示用户已创建待编制;2、未创建,提示用户待创建
        if(null != returnPlanVersion){
            //获取回款计划版本名称
            String versionName = VersionNameUtils.getVersionName(returnPlanVersion);
            //封装代办数据
            PortalTodoBo todoBo = new PortalTodoBo();
            todoBo.setTitle(fullPathOrgData.getProjectName() + fullPathOrgData.getStageName() + "有了新版回款计划版本：" + versionName + "，请编排该分期"+ versionTypeName +"回款计划");
            todoBo.setTodoType("编排"+ versionTypeName +"回款计划");
            todoBo.setTodoTypeCode(TodoTypeCodeEnum.RETURN_PLAN.getKey());
            todoBo.setContent(todoBo.getTitle());
            //待办组织类型 1 旭辉集团 2 区域或事业部 3 城市公司 4 项目 5分期
            todoBo.setOrgType(TodoOrgTypeEnum.FIVE.getKey());
            todoBo.setGroupId(fullPathOrgData.getGroupId());
            todoBo.setRegionId(fullPathOrgData.getRegionId());
            todoBo.setCityId(fullPathOrgData.getCityId());
            todoBo.setProjectId(fullPathOrgData.getProjectId());
            todoBo.setStagesId(stageId);
            //发送提示消息代办
            commonService.sendMessagePortalTodo(todoBo,idmUserList);

        }else {
            //发送代办消息代办
            String pcUrl = returnNoticePcUrl+fullPathOrgData.getRegionId()+"/"+fullPathOrgData.getCityId()+"/"+fullPathOrgData.getProjectId()+"/"+stageId+"/"+versionType;
            //获取回款计划版本名称
            String versionName = VersionNameUtils.getVersionName(signPlanVersion);
            String title = fullPathOrgData.getProjectName()+fullPathOrgData.getStageName()+"有了新版"+ versionTypeName +"签约计划:"+ versionName +"，请新建该分期" + versionTypeName + "回款计划";

            PortalTodoBo doorTodoBo = new PortalTodoBo();
            //移动端地址，钉钉上收到待办时，点击后进入的地址。（可为钉钉应用首页）必传
            doorTodoBo.setMobileTaskUrl(returnNoticeAppUrl);
            //PC端地址，门户portal.cifi.com.cn中点击一条待办后进入的地址。必传
            doorTodoBo.setPcTaskUrl(pcUrl);
            //待办的主题 自己定义
            doorTodoBo.setTitle(title);
            //任务类型，待办列表中显示在主题前面的中括号中，表示提醒审批人待审批的是什么。如“转交”，“驳回”，“合同审批”等，最多用四个字来总结
            doorTodoBo.setTodoType("新建"+ versionTypeName +"回款计划");
            //任务类型code
            doorTodoBo.setTodoTypeCode(TodoTypeCodeEnum.RETURN_PLAN.getKey());
            // 任务关联id [货值\供货\签约版本id]
            doorTodoBo.setVersionId(stageId+versionType);
            //任务关联id类型
            doorTodoBo.setVersionIdType(VersionIdTypeEnum.SIGN_PLAN.getKey());
            //待办说明
            doorTodoBo.setContent(title);
            //待办组织类型 1 旭辉集团 2 区域或事业部 3 城市公司 4 项目 5分期
            doorTodoBo.setOrgType(TodoOrgTypeEnum.FIVE.getKey());
            doorTodoBo.setGroupId(fullPathOrgData.getGroupId());
            doorTodoBo.setRegionId(fullPathOrgData.getRegionId());
            doorTodoBo.setCityId(fullPathOrgData.getCityId());
            doorTodoBo.setProjectId(fullPathOrgData.getProjectId());
            doorTodoBo.setStagesId(stageId);
            commonService.start("",doorTodoBo,idmUserList);
        }
    }

    @Override
    public PortalTodo getPortalTodo(String combinationId) {
        //查询代办
        PortalTodo portalTodo = portalTodoMapper.selectByVersionIdAndTodoTypeCode(combinationId,TodoTypeCodeEnum.RETURN_PLAN.getKey());
        if(null == portalTodo){
            return null;
        }
        String operatorType = portalTodo.getOperatorType();
        if(operatorType.equals("3") || operatorType.equals("4")){
            return null;
        }
        return portalTodo;
    }

    /**
     * 消除代办
     * @param versionId 代办关联id
     * @param operatorType 处理方式
     * @param handUser 处理人
     */
    @Override
    public String deleteTodoInfo(String versionId, String operatorType, IdmUser handUser) {
        //查询代办
        PortalTodo portalTodo = portalTodoMapper.selectByVersionIdAndTodoTypeCode(versionId,TodoTypeCodeEnum.RETURN_PLAN.getKey());
        if(null != portalTodo){
            String instanceId = portalTodo.getInstanceId();
            List<PortalTodoTask> list = portalTodoTaskMapper.selectByInstanceId(portalTodo.getInstanceId());
            if(CollectionUtils.isNotEmpty(list)) {
                for(PortalTodoTask todoTask : list) {
                    String taskApproveId = todoTask.getTaskApproveId();
                    IdmUser user = idmUserService.getByAlias(taskApproveId);
                    commonService.updatePortalProcess(instanceId, operatorType, user, user);
                }
            }else {
                commonService.updatePortalProcess(instanceId, operatorType, handUser, null);
            }
            return "消除成功";
        }
        return "未查询到此代办消息";
    }

    /**
     * 回款计划全盘概览
     * @param id
     * @param year
     * @param month
     * @return
     */
    @Override
    public List<ReturnPlanOverviewDataVo> fullOverview(String id, String year, String month) {
        //参数校验
        if(StringUtils.isBlank(id) || StringUtils.isBlank(year) || StringUtils.isBlank(month) ){
            throw new MsgException(CodeEnum.REQUEST_PARAM_NOT_FULL_ERROR);
        }
        ReturnPlanVersion returnPlanVersion = returnPlanVersionMapper.selectById(id);
        if(null == returnPlanVersion || returnPlanVersion.getIsDelete() == IsDeleteEnum.YES.getKey()){
            throw new MsgException(CodeEnum.NOT_RETURN_PLAN_VERSION_ERROR);
        }

        List<ReturnPlanOverviewDataVo> voList = new ArrayList<>();
        String yearMonth = LocalDateTimeUtils.toStrYearMonth(Integer.valueOf(year),Integer.valueOf(month));
        String nextYear = String.valueOf(Integer.valueOf(year)+1);

        //查询项目下各分期最新的回款计划版本列表
        List<ReturnPlanVersion> returnPlanVersionList = returnPlanVersionMapper.selectStageIdListByProjectIdGroupByStageIdOrderByCreateTimeDesc(returnPlanVersion.getProjectId(), IsDeleteEnum.NO.getKey());
        //获取回款计划版本Map
        Map<String, ReturnPlanVersion> returnPlanVersionMap = returnPlanVersionList.stream().collect(Collectors.toMap(ReturnPlanVersion::getId, s -> s));
        //获取回款计划详情列表
        List<ReturnPlanVersionDetail> returnPlanVersionDetailList = returnPlanVersionDetailWrapper.selectByVersionIdList(new ArrayList<>(returnPlanVersionMap.keySet()), IsDeleteEnum.NO.getKey());
        //以回款版本id、组团id、组合业态编码、分组（key:回款版本id,value:回款计划详情列表）
        Map<String, Map<String, Map<String, List<ReturnPlanVersionDetail>>>> returnPlanVersionDetailByVersionIdMap = returnPlanVersionDetailList.stream()
                .collect(Collectors.groupingBy(ReturnPlanVersionDetail::getVersionId,
                        Collectors.groupingBy(ReturnPlanVersionDetail::getGroupId,
                                Collectors.groupingBy(ReturnPlanVersionDetail::getCombinationProductCode))));

        for(Map.Entry<String, Map<String, Map<String, List<ReturnPlanVersionDetail>>>> returnPlanVersionEntry : returnPlanVersionDetailByVersionIdMap.entrySet()){
            String versionId = returnPlanVersionEntry.getKey();
            ReturnPlanVersion version = returnPlanVersionMap.get(versionId);

            Map<String, Map<String, List<ReturnPlanVersionDetail>>> returnPlanVersionDetailByGroupIdMap = returnPlanVersionEntry.getValue();

            for(Map.Entry<String, Map<String, List<ReturnPlanVersionDetail>>> groupEntry : returnPlanVersionDetailByGroupIdMap.entrySet()){
                String groupId = groupEntry.getKey();
                Map<String, List<ReturnPlanVersionDetail>> returnPlanVersionDetailByCombinationProductCodeMap = groupEntry.getValue();

                for(Map.Entry<String, List<ReturnPlanVersionDetail>> combinationProductCodeEntry : returnPlanVersionDetailByCombinationProductCodeMap.entrySet()){
                    String combinationProductCode = combinationProductCodeEntry.getKey();
                    List<ReturnPlanVersionDetail> returnPlanVersionDetails = combinationProductCodeEntry.getValue();
                    if(CollectionUtils.isEmpty(returnPlanVersionDetails)){
                        continue;
                    }

                    //获取年月回款合计
                    BigDecimal monthlyReturnedMoney = returnPlanVersionDetailService.getTotalReturnAmountByYearMonth(yearMonth,returnPlanVersionDetails);
                    //获取当年回款合计
                    BigDecimal annualReturnedMoney = returnPlanVersionDetailService.getTotalReturnAmountByYear(year,returnPlanVersionDetails);
                    //获取次年回款合计
                    BigDecimal nextYearReturnedMoney = returnPlanVersionDetailService.getTotalReturnAmountByYear(nextYear,returnPlanVersionDetails);
                    //获取最后一个月的期末应收
                    BigDecimal residualReceivables = returnPlanVersionDetailService.getLastMonthFinalReceivable(returnPlanVersionDetails);

                    ReturnPlanVersionDetail detail = returnPlanVersionDetails.get(0);

                    ReturnPlanOverviewDataVo vo = new ReturnPlanOverviewDataVo();
                    vo.setCombinationProductCode(combinationProductCode);
                    vo.setVersionId(versionId);
                    vo.setProjectName(version.getProjectName());
                    vo.setStageName(version.getStageName());
                    vo.setGroupId(groupId);
                    vo.setProductCode(detail.getProductCode());
                    vo.setProductName(detail.getProductName());
                    vo.setBusinessType(detail.getBusinessType());
                    vo.setBusinessTypeName(detail.getBusinessTypeName());
                    vo.setFreeType(detail.getFreeType());
                    vo.setFreeTypeName(detail.getFreeTypeName());
                    vo.setProType(detail.getProType());
                    vo.setProTypeName(detail.getProTypeName());
                    vo.setMonthlyReturnedMoney(monthlyReturnedMoney);
                    vo.setAnnualReturnedMoney(annualReturnedMoney);
                    vo.setNextYearReturnedMoney(nextYearReturnedMoney);
                    vo.setResidualReceivables(residualReceivables);
                    voList.add(vo);
                }
            }
        }
        return voList;
    }

    /**
     * 查询回款计划审批预览数据
     * @param projectId 项目id
     * @param approvalId 审批id
     * @return
     */
    @Override
    public ReturnPlanApprovalPreviewVo getApprovalPreview(String projectId, String approvalId) {
        //参数校验
        if(StringUtils.isBlank(projectId) && StringUtils.isBlank(approvalId)) {
            throw new MsgException(CodeEnum.REQUEST_PARAM_NOT_FULL_ERROR);
        }

        //查询审批单
        ApprovalBill approvalBill = null;
        if(StringUtils.isBlank(projectId)){
            approvalBill = approvalBillService.getById(approvalId);
        }else {
            approvalBill = approvalBillService.getNotSubmitApprovalBill(projectId, ProcessTypeEnum.MONEY_BACK_PLAN.getKey());
        }

        //生成审批预览内容
        if(null != approvalBill){
            List<String> approvalStateList = Arrays.asList(ApprovedStateNameCodeEnum.APPROVAL.getKey(), ApprovedStateNameCodeEnum.APPROVAL_SUCCESS.getKey());
            if(approvalStateList.contains(approvalBill.getState())){
                //审批完成、审批中，取审批预览表内容
                String content = approvalPreviewService.getContentByApprovalId(approvalBill.getId());
                return JSON.parseObject(content, ReturnPlanApprovalPreviewVo.class);
            }else {
                //计算生成审批预览内容
                return getReturnPlanApprovalPreviewVo(approvalBill.getProjectId());
            }
        }else {
            //计算生成审批预览内容
            return getReturnPlanApprovalPreviewVo(projectId);
        }
    }

    /**
     * 计算生成审批预览内容
     * @param projectId
     * @return
     */
    private ReturnPlanApprovalPreviewVo getReturnPlanApprovalPreviewVo(String projectId) {
        ReturnPlanApprovalPreviewVo vo = new ReturnPlanApprovalPreviewVo();

        //获取项目数据
        MdmProject mdmProject = projectMapper.selectByProjectId(projectId, IsDeleteEnum.NO.getKey());
        FullPathOrgData fullPathOrgData = projectByStagesService.getParentFullPathOrgData(projectId);

        //获取待提交版本列表
        List<ReturnPlanVersion> toSubmitVersionList = getToSubmitVersionList(projectId);
        //获取回款审批相关数据
        ReturnApprovalBO returnApprovalBO = getReturnApprovalBO(projectId,fullPathOrgData.getRegionId(),toSubmitVersionList);

        //获取权益比例
        String equityRatioRate = projectBasicService.getEquityRatioRate(projectId);
        //获取待提交版本id列表
        List<String> versionIdList = toSubmitVersionList.stream().map(ReturnPlanVersion::getId).collect(Collectors.toList());
        //获取本次编辑版本列表
        List<String> editedVersionList = getEditedVersionList(toSubmitVersionList);
        //获取区域年度回款目标
        List<ReturnPlanRegionYearTargetVo> regionYearTargetVoList = getRegionYearTargetVoList(returnApprovalBO);
        //项目年度回款指标列表
        List<ReturnPlanProjectYearTargetVo> projectYearTargetVoList = getProjectYearTargetVoList(returnApprovalBO);
        //回款系数差异
        List<ReturnPlanCoeffcienDifferenceVo> coeffcienDifferenceVoList = getCoeffcienDifferenceVoList(returnApprovalBO);
        //获取审批流程变量
        JSONObject vars = getApprovalVars(projectId,toSubmitVersionList,versionIdList);

        ApprovalBill approvalBill = approvalBillService.getNotSubmitApprovalBill(projectId, ProcessTypeEnum.MONEY_BACK_PLAN.getKey());
        if(null != approvalBill){
            // 查询上传附件
            List<FilePreviewVO> files = fileUploadService.getFilePreview(approvalBill.getId());
            String instanceId = approvalBill.getInstanceId();
            String taskId = approvalBill.getTaskId();
            String revoewUrl =approvalBillService.getReviewUrl(instanceId,taskId);
            vo.setApprovalId(approvalBill.getId());
            vo.setApprovalId(approvalBill.getId());
            vo.setInstanceId(instanceId);
            vo.setTaskId(taskId);
            vo.setState(approvalBill.getState());
            vo.setReviewUrl(revoewUrl);
            vo.setFiles(files);
            vo.setDescription(approvalBill.getDescription());
        }
        vo.setProjectId(projectId);
        vo.setProjectName(mdmProject.getProjectName());
        vo.setProjectCode(mdmProject.getProjectCode());
        vo.setRegionName(fullPathOrgData.getRegionName());
        vo.setEquityRatio(equityRatioRate);
        vo.setVersionIdList(versionIdList);
        vo.setEditedVersionList(editedVersionList);
        vo.setRegionYearTargetVoList(regionYearTargetVoList);
        vo.setProjectYearTargetVoList(projectYearTargetVoList);
        vo.setCoeffcienDifferenceVoList(coeffcienDifferenceVoList);
        vo.setVars(vars);




        return vo;
    }

    /**
     * 获取审批流程变量
     * @param projectId 项目id
     * @param toSubmitVersionList 待提交版本列表
     * @param versionIdList 待提交版本id列表
     * @return
     */
    private JSONObject getApprovalVars(String projectId, List<ReturnPlanVersion> toSubmitVersionList, List<String> versionIdList) {
        String versionType = "";
        int isAssessment = 0;
        int isModified = 0;

        //获取审批分支参数
        List<String> versionTypeList = toSubmitVersionList.stream().map(ReturnPlanVersion::getVersionType).collect(Collectors.toList());
        //审批优先级  动态版 > 战规版 > 投资版 > 预算版
        if(versionTypeList.contains(VersionTypeEunm.DYNAMIC.getKey())){
            versionType = VersionTypeEunm.DYNAMIC.getKey();
        }else if(versionTypeList.contains(VersionTypeEunm.WARPLAN.getKey())){
            versionType = VersionTypeEunm.WARPLAN.getKey();
        }else if(versionTypeList.contains(VersionTypeEunm.INVESTMENT.getKey())){
            versionType = VersionTypeEunm.INVESTMENT.getKey();
        }
        long assessmentCount = toSubmitVersionList.stream().filter(s -> VersionTypeEunm.INVESTMENT.getKey().equals(s.getVersionType()) && WhetherEnum.YES.getKey() == s.getIsAssessment()).count();
        isAssessment = assessmentCount > 0 ? WhetherEnum.YES.getKey() : WhetherEnum.NO.getKey();
        //查询回款计划详情列表
        List<ReturnPlanVersionDetail> returnPlanVersionDetailList = returnPlanVersionDetailWrapper.selectByVersionIdList(versionIdList, IsDeleteEnum.NO.getKey());
        long adjustCount = returnPlanVersionDetailList.stream()
                .filter(s -> s.getIncrementReturnAmountAdjust().compareTo(BigDecimal.ZERO) != 0
                        || s.getStoreReturnAmountAdjust().compareTo(BigDecimal.ZERO) != 0).count();
        isModified = adjustCount > 0 ? WhetherEnum.YES.getKey() : WhetherEnum.NO.getKey();

        JSONObject vars = new JSONObject();
        vars.put("projectId",projectId);
        vars.put("versionType",versionType);
        vars.put("isAssessment",isAssessment);
        vars.put("isModified",isModified);
        return vars;
    }

    /**
     * 项目年度回款指标列表
     * @param returnApprovalBO 回款审批相关数据
     * @return
     */
    private List<ReturnPlanProjectYearTargetVo> getProjectYearTargetVoList(ReturnApprovalBO returnApprovalBO) {
        List<ReturnPlanProjectYearTargetVo> voList = new ArrayList<>();

        List<ReturnPlanVersionDetail> versionDeatilList = returnApprovalBO.getProjectApprovalOverallVersionDeatilList();
        LocalDateTime returnTime = versionDeatilList.stream()
                .filter(s -> s.getSignAmount().compareTo(BigDecimal.ZERO) != 0)
                .min(Comparator.comparing(ReturnPlanVersionDetail::getReturnTime)).orElse(new ReturnPlanVersionDetail()).getReturnTime();

        //三年列表
        List<Integer> yearList = new ArrayList<>();
        LocalDateTime now = LocalDateTime.now();
        if(returnTime.compareTo(now) > 0){
            yearList = Arrays.asList(returnTime.getYear(), returnTime.plusYears(1).getYear(),returnTime.plusYears(2).getYear());
        }else {
            yearList = Arrays.asList(now.getYear(), now.plusYears(1).getYear(),now.plusYears(2).getYear());
        }

        for(int year : yearList){
            //项目全盘年度回款指标列表
            ReturnPlanProjectOverallYearTargetVo projectOverallYearTargetVo = getProjectOverallYearTargetVo(returnApprovalBO,year);
            //年度回款指标详情
            ReturnPlanYearTargetDetailVo yearTargetDetailVo = getYearTargetDetailVo(returnApprovalBO,year);
            //月度回款指标详情
            List<ReturnPlantMonthTargetDetailVo> monthTargetDetailVoList = getMonthTargetDetailVoList(returnApprovalBO,year);

            ReturnPlanProjectYearTargetVo vo = new ReturnPlanProjectYearTargetVo();
            vo.setYear(year);
            vo.setProjectOverallYearTarget(projectOverallYearTargetVo);
            vo.setYearTargetDetailVo(yearTargetDetailVo);
            vo.setMonthTargetDetailVoList(monthTargetDetailVoList);
            voList.add(vo);
        }
        return voList;
    }

    /**
     * 回款系数差异
     * @param returnApprovalBO 回款审批相关数据
     * @return
     */
    private List<ReturnPlanCoeffcienDifferenceVo> getCoeffcienDifferenceVoList(ReturnApprovalBO returnApprovalBO) {
        String projectId = returnApprovalBO.getProjectId();

        List<String> projectApprovalOverallVersionIdList = returnApprovalBO.getProjectApprovalOverallVersionIdList();
        //获取回款系数列表
        List<ReturnCoefficientDetailDto> coefficientDetailList = returnPlanVersionCoefficientService.getReturnCoefficientDetailListByReturnPlanVersionIdList(projectApprovalOverallVersionIdList);
        //以版本id分组
        Map<String, List<ReturnCoefficientDetailDto>> returnCoefficientDetailMap = coefficientDetailList.stream()
                .collect(Collectors.groupingBy(ReturnCoefficientDetailDto::getStageName));


        //回款系数差异列表
        List<ReturnPlanCoeffcienDifferenceVo> coeffcienDifferenceVoList = new ArrayList<>();

        //1、对比项目实际版差异
        ReturnPlanCoeffcienDifferenceVo vsProjectPracticalVo= new ReturnPlanCoeffcienDifferenceVo();
        //获取项目最新回款系数版本的回款系数详情列表
        List<ReturnCoefficientDetail> projectPracticalCoefficientList = projectReturnCoefficientMapper.selectNewVersionCoefficientDetailList(projectId, ReturnVersionTypeEnum.PRACTICAL.getKey(), VersionStatusEnum.SYSGENERATION.getKey(),IsDeleteEnum.NO.getKey());
        //分期回款系数差异列表
        List<ReturnPlanStageCoeffcienDifferenceVo> vsProjectPracticalVoList = getStageCoeffcienDifferenceVoList(returnCoefficientDetailMap,projectPracticalCoefficientList);
        vsProjectPracticalVo.setName("VS项目实际版");
        vsProjectPracticalVo.setStageCoeffcienDifferenceVoList(vsProjectPracticalVoList);
        if(CollectionUtils.isNotEmpty(vsProjectPracticalVoList)){
            coeffcienDifferenceVoList.add(vsProjectPracticalVo);
        }

        //2、对比城市实际版差异
        ReturnPlanCoeffcienDifferenceVo vsCityPracticalVo= new ReturnPlanCoeffcienDifferenceVo();
        //获取城市最新回款系数版本的回款系数详情列表
        List<ReturnCoefficientDetail> practicalCoefficientList = getCityPracticalCoefficientList(projectId);
        //分期回款系数差异列表
        List<ReturnPlanStageCoeffcienDifferenceVo> vsCityPracticalVoList = getStageCoeffcienDifferenceVoList(returnCoefficientDetailMap,practicalCoefficientList);
        vsCityPracticalVo.setName("VS城市实际版");
        vsCityPracticalVo.setStageCoeffcienDifferenceVoList(vsCityPracticalVoList);
        if(CollectionUtils.isNotEmpty(vsCityPracticalVoList)){
            coeffcienDifferenceVoList.add(vsCityPracticalVo);
        }
        return coeffcienDifferenceVoList;
    }

    /**
     * 获取城市最新回款系数版本的回款系数详情列表
     * @param projectId 项目id
     * @return
     */
    private List<ReturnCoefficientDetail> getCityPracticalCoefficientList(String projectId) {
        MdmProject mdmProject = projectMapper.selectByProjectId(projectId,IsDeleteEnum.NO.getKey());
        String companyId = mdmProject.getCompanyId();
        return dmCityReturnCoefficientMapper.selectByCityId(companyId);
    }

    /**
     * 获取分期回款系数差异列表
     * @param returnCoefficientDetailMap 回款系数详情Map
     * @param vsCoefficientList 对比回款系数详情列表
     * @return
     */
    private List<ReturnPlanStageCoeffcienDifferenceVo> getStageCoeffcienDifferenceVoList(Map<String, List<ReturnCoefficientDetailDto>> returnCoefficientDetailMap, List<ReturnCoefficientDetail> vsCoefficientList) {
        List<ReturnPlanStageCoeffcienDifferenceVo> stageCoeffcienDifferenceVoList = new ArrayList<>();
        //参数校验
        if(MapUtils.isEmpty(returnCoefficientDetailMap) || CollectionUtils.isEmpty(vsCoefficientList)){
            return stageCoeffcienDifferenceVoList;
        }

        //以标准业态分组(key:标准业态编码 value:回款系数详情)
        Map<String, ReturnCoefficientDetail> vsCoefficientDetailMap = vsCoefficientList.stream()
                .collect(Collectors.toMap(ReturnCoefficientDetail::getProProductCode,s->s));

        for(Map.Entry<String, List<ReturnCoefficientDetailDto>> stageEntry : returnCoefficientDetailMap.entrySet()){
            ReturnPlanStageCoeffcienDifferenceVo stageCoeffcienDifferenceVo = new ReturnPlanStageCoeffcienDifferenceVo();
            List<ReturnPlanCoeffcienDifferenceDetailVo> voList = new ArrayList<>();

            //分期名称
            String stageName = stageEntry.getKey();
            //该分期版本回款系数详情列表
            List<ReturnCoefficientDetailDto> coefficientList = stageEntry.getValue();
            //以标准业态分组(key:标准业态编码 value:回款系数详情)
            Map<String, ReturnCoefficientDetailDto> coefficientDetailMap = coefficientList.stream()
                    .collect(Collectors.toMap(ReturnCoefficientDetailDto::getProProductCode,s->s));
            for(Map.Entry<String, ReturnCoefficientDetailDto> entry : coefficientDetailMap.entrySet()){
                ReturnPlanCoeffcienDifferenceDetailVo vo = new ReturnPlanCoeffcienDifferenceDetailVo();

                //标准业态编码
                String proProductCode = entry.getKey();
                //回款系数详情
                ReturnCoefficientDetailDto coefficientDetail = entry.getValue();
                //对比回款系数详情
                ReturnCoefficientDetail vsCoefficientDetail = vsCoefficientDetailMap.get(proProductCode);

                Map<Integer, BigDecimal> calculateCoefficientMap = coefficientDetail.getCoefficientMap();
                Map<Integer, Object> actualCoefficientMap = vsCoefficientDetail == null ? MyMapUtils.getCoefficientMap() : vsCoefficientDetail.getCoefficientOneMap();
                Map<Integer, Object> differenceCoefficientMap = new HashMap<>();
                for(Map.Entry<Integer, BigDecimal> calculateEntry : calculateCoefficientMap.entrySet()){
                    Integer num = calculateEntry.getKey();
                    BigDecimal calculateValue = calculateEntry.getValue();
                    Object actualValue = actualCoefficientMap.get(num);
                    if("-".equals(actualValue)){
                        differenceCoefficientMap.put(num,"-");
                    }else {
                        differenceCoefficientMap.put(num,calculateValue.subtract((BigDecimal) actualValue));
                    }
                }
                vo.setTypeName(BigStandardFormatsEnum.getValueByKey(StandardFormatsMapEnum.getValueByKey(coefficientDetail.getProProductCode())));
                vo.setProProductType(coefficientDetail.getProProductType());
                vo.setCalculateCoefficientMap(calculateCoefficientMap);
                vo.setActualCoefficientMap(actualCoefficientMap);
                vo.setDifferenceCoefficientMap(differenceCoefficientMap);
                voList.add(vo);
            }
            stageCoeffcienDifferenceVo.setStageName(stageName);
            stageCoeffcienDifferenceVo.setCoeffcienDifferenceDetailVoList(voList);
            stageCoeffcienDifferenceVoList.add(stageCoeffcienDifferenceVo);
        }
        return stageCoeffcienDifferenceVoList;

    }

    /**
     * 月度回款指标详情
     * @param returnApprovalBO 回款审批相关数据
     * @param year
     * @return
     */
    private List<ReturnPlantMonthTargetDetailVo> getMonthTargetDetailVoList(ReturnApprovalBO returnApprovalBO, int year) {
        List<ReturnPlanVersion> versionList = returnApprovalBO.getProjectApprovalOverallVersionList();
        List<ReturnPlanVersionDetail> versionDeatilList = returnApprovalBO.getProjectApprovalOverallVersionDeatilList();
        Map<String, Map<Integer, List<ReturnPlanVersionDetail>>> versionDeatilListByVersionIdMap = versionDeatilList.stream()
                .filter(s -> year == s.getReturnTime().getYear())
                .sorted(Comparator.comparing(ReturnPlanVersionDetail::getReturnTime))
                .collect(Collectors.groupingBy(ReturnPlanVersionDetail::getVersionId,
                        Collectors.groupingBy(s -> s.getReturnTime().getMonthValue())));

        List<ReturnPlantMonthTargetDetailVo> voList = new ArrayList<>();

        for (int i = 1; i <=12 ; i++) {
            ReturnPlantMonthTargetDetailVo vo = new ReturnPlantMonthTargetDetailVo();
            //月初应收账款
            BigDecimal monthEarlyReceivable = BigDecimal.ZERO;
            //月度签约
            BigDecimal monthSign = BigDecimal.ZERO;
            //月度实际数-增量回款
            BigDecimal monthlyActualIncrementalReturn = BigDecimal.ZERO;
            //月度实际数-存量回款
            BigDecimal monthlyActualStoreReturn = BigDecimal.ZERO;
            //月度测算数-增量回款
            BigDecimal monthlyCalculateIncrementalReturn = BigDecimal.ZERO;
            //月度测算数-存量回款
            BigDecimal monthlyCalculateStoreReturn = BigDecimal.ZERO;
            //月度调整数-增量回款
            BigDecimal monthlyAdjustIncrementalReturn = BigDecimal.ZERO;
            //月度调整数-存量回款
            BigDecimal monthlyAdjustStoreReturn = BigDecimal.ZERO;
            //月度已签未回回款数
            BigDecimal monthSignNotReturn = BigDecimal.ZERO;
            //期初应退回款数
            BigDecimal refundable = BigDecimal.ZERO;
            //月度回款合计-增量回款
            BigDecimal monthlyTotalIncrementalReturn = BigDecimal.ZERO;
            //月度回款合计-存量回款
            BigDecimal monthlyTotalStoreReturn = BigDecimal.ZERO;
            //月度回款合计-回款合计
            BigDecimal monthlyTotalReturnTotal = BigDecimal.ZERO;
            //月度回款合计-月末应收账款
            BigDecimal monthlyTotalMonthEndReceivable = BigDecimal.ZERO;
            //累计年度签约合计
            BigDecimal yearSignAmountTotal = BigDecimal.ZERO;
            //累计签约合计
            BigDecimal signAmountTotal = BigDecimal.ZERO;
            //累计年度回款合计
            BigDecimal yearReturnAmountTotal = BigDecimal.ZERO;
            //累计回款合计
            BigDecimal returnAmountTotal = BigDecimal.ZERO;
            //1月月初应收账款
            BigDecimal beginningReceivable = BigDecimal.ZERO;

            for(ReturnPlanVersion version : versionList) {
                //所属时间
                LocalDateTime affiliationTime = LocalDateTimeUtils.toLocalDateTime(version.getAffiliationMonth());
                Map<Integer, List<ReturnPlanVersionDetail>> monthListMap = versionDeatilListByVersionIdMap.get(version.getId());
                List<ReturnPlanVersionDetail> versionDetails = monthListMap.get(i);
                if(CollectionUtils.isEmpty(versionDetails)){
                    continue;
                }
                //计算1月期初应收
                if(1 == i){
                    beginningReceivable = beginningReceivable.add(versionDetails.stream()
                            .map(ReturnPlanVersionDetail::getEarlyReceivableAmount)
                            .reduce(BigDecimal.ZERO, BigDecimal::add));
                }

                for (ReturnPlanVersionDetail detail : versionDetails) {
                    monthEarlyReceivable = monthEarlyReceivable.add(detail.getEarlyReceivableAmount());

                    if(LocalDateTimeUtils.compareDate(detail.getReturnTime(),affiliationTime,0)){
                        monthSign = monthSign.add(detail.getActualSignAmount());
                    }else {
                        monthSign = monthSign.add(detail.getPlanSignAmount());
                    }
                    monthlyActualIncrementalReturn = monthlyActualIncrementalReturn.add(detail.getIncrementReturnAmountActual());
                    monthlyActualStoreReturn = monthlyActualStoreReturn.add(detail.getStoreReturnAmountActual());
                    monthlyCalculateIncrementalReturn = monthlyCalculateIncrementalReturn.add(detail.getIncrementReturnAmountCalculate());
                    monthlyCalculateStoreReturn = monthlyCalculateStoreReturn.add(detail.getStoreReturnAmountCalculate());
                    monthlyAdjustIncrementalReturn = monthlyAdjustIncrementalReturn.add(detail.getIncrementReturnAmountAdjust());
                    monthlyAdjustStoreReturn = monthlyAdjustStoreReturn.add(detail.getStoreReturnAmountAdjust());
                    monthSignNotReturn = monthSignNotReturn.add(detail.getSignedUnrecovered());
                    refundable = refundable.add(detail.getRefundable());
                    monthlyTotalIncrementalReturn = monthlyTotalIncrementalReturn.add(detail.getIncrementReturnAmount());
                    monthlyTotalStoreReturn = monthlyTotalStoreReturn.add(detail.getStoreReturnAmount());
                    monthlyTotalReturnTotal = monthlyTotalReturnTotal.add(detail.getTotalReturnAmount());
                    monthlyTotalMonthEndReceivable = monthlyTotalMonthEndReceivable.add(detail.getFinalReceivable());
                    yearSignAmountTotal = yearSignAmountTotal.add(detail.getYearSignAmountTotal());
                    signAmountTotal = signAmountTotal.add(detail.getSignAmountTotal());
                    yearReturnAmountTotal = yearReturnAmountTotal.add(detail.getYearReturnAmountTotal());
                    returnAmountTotal = returnAmountTotal.add(detail.getReturnAmountTotal());
                }
            }
            vo.setMonth(i);
            vo.setMonthEarlyReceivable(monthEarlyReceivable);
            vo.setMonthSign(monthSign);
            vo.setMonthlyActualIncrementalReturn(monthlyActualIncrementalReturn);
            vo.setMonthlyActualStoreReturn(monthlyActualStoreReturn);
            vo.setMonthlyCalculateIncrementalReturn(monthlyCalculateIncrementalReturn);
            vo.setMonthlyCalculateStoreReturn(monthlyCalculateStoreReturn);
            vo.setMonthlyAdjustIncrementalReturn(monthlyAdjustIncrementalReturn);
            vo.setMonthlyAdjustStoreReturn(monthlyAdjustStoreReturn);
            vo.setMonthSignNotReturn(monthSignNotReturn);
            vo.setRefundable(refundable);
            vo.setMonthlyTotalIncrementalReturn(monthlyTotalIncrementalReturn);
            vo.setMonthlyTotalStoreReturn(monthlyTotalStoreReturn);
            vo.setMonthlyTotalReturnTotal(monthlyTotalReturnTotal);
            vo.setMonthlyTotalMonthEndReceivable(monthlyTotalMonthEndReceivable);
            vo.setYearSignAmountTotal(yearSignAmountTotal);
            vo.setSignAmountTotal(signAmountTotal);
            vo.setYearReturnAmountTotal(yearReturnAmountTotal);
            vo.setReturnAmountTotal(returnAmountTotal);
            //累计签约回款率(开售月累计到当月回款合计/开售月累计到累计当月签约金额)
            BigDecimal signReturnRate = BigDecimalUtils.divBigDecimal(returnAmountTotal,signAmountTotal,5);
            //年度综合回款率(从当年1月累计到当月回款合计/(当年1月的期初应收 + 从当年1月累计到累计当月签约金额)
            BigDecimal annualOverallReturnRate = BigDecimalUtils
                    .divBigDecimal(yearReturnAmountTotal,beginningReceivable.add(yearSignAmountTotal),5);
            //累计签约回款率
            vo.setSignReturnRate(signReturnRate);
            //年度综合回款率
            vo.setAnnualOverallReturnRate(annualOverallReturnRate);
            voList.add(vo);
        }

        return voList;
    }

    /**
     * 年度回款指标详情
     * @param returnApprovalBO 回款审批相关数据
     * @param year
     * @return
     */
    private ReturnPlanYearTargetDetailVo getYearTargetDetailVo(ReturnApprovalBO returnApprovalBO, int year) {
        List<ReturnPlanVersion> versionList = returnApprovalBO.getProjectApprovalOverallVersionList();
        List<ReturnPlanVersionDetail> versionDeatilList = returnApprovalBO.getProjectApprovalOverallVersionDeatilList();
        Map<String, List<ReturnPlanVersionDetail>> versionDeatilListMap = versionDeatilList.stream()
                .filter(s-> year == s.getReturnTime().getYear())
                .sorted(Comparator.comparing(ReturnPlanVersionDetail::getReturnTime))
                .collect(Collectors.groupingBy(ReturnPlanVersionDetail::getVersionId));

        //年度签约
        BigDecimal yearSign = BigDecimal.ZERO;
        //年度实际数-增量回款
        BigDecimal annualActualIncrementalReturn  = BigDecimal.ZERO;
        //年度实际数-存量回款
        BigDecimal annualActualStoreReturn  = BigDecimal.ZERO;
        //年度测算数-增量回款
        BigDecimal annualCalculateIncrementalReturn  = BigDecimal.ZERO;
        //年度测算数-存量回款
        BigDecimal annualCalculateStoreReturn  = BigDecimal.ZERO;
        //年度调整数-增量回款
        BigDecimal annualAdjustIncrementalReturn  = BigDecimal.ZERO;
        //年度调整数-存量回款
        BigDecimal annualAdjustStoreReturn  = BigDecimal.ZERO;
        //年度已签未回回款数
        BigDecimal yearSignNotReturn  = BigDecimal.ZERO;
        //年度回款合计-增量回款
        BigDecimal annualTotalIncrementalReturn  = BigDecimal.ZERO;
        //年度回款合计-存量回款
        BigDecimal annualTotalStoreReturn  = BigDecimal.ZERO;
        //年度回款合计-回款合计
        BigDecimal annualTotalReturnTotal  = BigDecimal.ZERO;
        //年初应收账款
        BigDecimal earlyReceivableAmount = BigDecimal.ZERO;
        //年末应收账款
        BigDecimal annualTotalYearEndReceivable = BigDecimal.ZERO;

        for(ReturnPlanVersion version : versionList){
            //所属时间
            LocalDateTime affiliationTime = LocalDateTimeUtils.toLocalDateTime(version.getAffiliationMonth());
            List<ReturnPlanVersionDetail> versionDetails = versionDeatilListMap.get(version.getId());
            for(ReturnPlanVersionDetail detail : versionDetails){
                if(detail.getReturnTime().getMonthValue() == 1){
                    earlyReceivableAmount = earlyReceivableAmount.add(detail.getEarlyReceivableAmount());
                }
                if(detail.getReturnTime().getMonthValue() == 12){
                    annualTotalYearEndReceivable = annualTotalYearEndReceivable.add(detail.getFinalReceivable());
                }

                if(LocalDateTimeUtils.compareDate(detail.getReturnTime(),affiliationTime,0)){
                    annualActualIncrementalReturn = annualActualIncrementalReturn.add(detail.getIncrementReturnAmountActual());
                    annualActualStoreReturn = annualActualStoreReturn.add(detail.getStoreReturnAmountActual());
                }else {
                    annualCalculateIncrementalReturn = annualCalculateIncrementalReturn.add(detail.getIncrementReturnAmountCalculate());
                    annualCalculateStoreReturn = annualCalculateStoreReturn.add(detail.getStoreReturnAmountCalculate());
                    annualAdjustIncrementalReturn = annualAdjustIncrementalReturn.add(detail.getIncrementReturnAmountAdjust());
                    annualAdjustStoreReturn = annualAdjustStoreReturn.add(detail.getStoreReturnAmountAdjust());
                }
                yearSign = yearSign.add(detail.getSignAmount());
                annualTotalIncrementalReturn = annualTotalIncrementalReturn.add(detail.getIncrementReturnAmount());
                annualTotalStoreReturn = annualTotalStoreReturn.add(detail.getStoreReturnAmount());
                annualTotalReturnTotal = annualTotalReturnTotal.add(detail.getTotalReturnAmount());
            }
        }
        yearSignNotReturn = earlyReceivableAmount;
        ReturnPlanYearTargetDetailVo vo = new ReturnPlanYearTargetDetailVo();

        vo.setYearSign(yearSign);
        vo.setAnnualActualIncrementalReturn(annualActualIncrementalReturn);
        vo.setAnnualActualStoreReturn(annualActualStoreReturn);
        vo.setAnnualCalculateIncrementalReturn(annualCalculateIncrementalReturn);
        vo.setAnnualCalculateStoreReturn(annualCalculateStoreReturn);
        vo.setAnnualAdjustIncrementalReturn(annualAdjustIncrementalReturn);
        vo.setAnnualAdjustStoreReturn(annualAdjustStoreReturn);
        vo.setYearSignNotReturn(yearSignNotReturn);
        vo.setAnnualTotalIncrementalReturn(annualTotalIncrementalReturn);
        vo.setAnnualTotalStoreReturn(annualTotalStoreReturn);
        vo.setAnnualTotalReturnTotal(annualTotalReturnTotal);
        vo.setYearEarlyReceivable(earlyReceivableAmount);
        vo.setAnnualTotalYearEndReceivable(annualTotalYearEndReceivable);
        return vo;
    }

    /**
     * 获取回款审批相关数据
     * @param projectId
     * @param regionId
     * @param toSubmitVersionList
     * @return
     */
    private ReturnApprovalBO getReturnApprovalBO(String projectId, String regionId, List<ReturnPlanVersion> toSubmitVersionList) {
        //获取项目下待审批版本列表Map(key:分期id,value:版本id)
        Map<String, String> toSubmitVersionMap = toSubmitVersionList.stream()
                .collect(Collectors.toMap(ReturnPlanVersion::getStageId, ReturnPlanVersion::getId));
        List<String>  projectApprovalOverallVersionIdList = toSubmitVersionList.stream().map(ReturnPlanVersion :: getId).collect(Collectors.toList());

        //获取区域全盘版本Map(key:项目id,value:(key:分期id,value:版本id))
        Map<String, Map<String, String>> regionOverallVersionMap = getRegionOverallVersionMap(regionId);
        //获取项目全盘最新版本Id列表
        List<String> projectOverallVersionIdList = new ArrayList<>();
        //区域全盘版本id列表
        List<String> regionOverallVersionIdList = new ArrayList<>();
        if(CollectionUtils.isNotEmpty(regionOverallVersionMap) && CollectionUtils.isNotEmpty(regionOverallVersionMap.get(projectId))){
            //获取项目全盘最新版本列表Map(key:分期id,value:版本id)
            Map<String, String> projectVersionMap = regionOverallVersionMap.get(projectId);
            //获取项目审批全盘最新版本列表
            projectOverallVersionIdList = regionOverallVersionMap.get(projectId).entrySet()
                    .stream().map(Map.Entry::getValue).collect(Collectors.toList());
            //将待审批版本替换入区域全盘版本Map
            toSubmitVersionMap.forEach(projectVersionMap::put);
            //获取项目审批全盘最新版本列表
            projectApprovalOverallVersionIdList = projectVersionMap.entrySet()
                    .stream().map(Map.Entry::getValue)
                    .collect(Collectors.toList());
            //区域全盘版本id列表
            regionOverallVersionIdList = regionOverallVersionMap.entrySet().stream()
                    .flatMap(s -> s.getValue().entrySet().stream().map(Map.Entry::getValue))
                    .collect(Collectors.toList());
        }else {
            regionOverallVersionMap.put(projectId,toSubmitVersionMap);
            //区域全盘版本id列表
            regionOverallVersionIdList = regionOverallVersionMap.entrySet().stream()
                    .flatMap(s -> s.getValue().entrySet().stream().map(Map.Entry::getValue))
                    .collect(Collectors.toList());
        }

        //查询回款计划列表
        List<ReturnPlanVersion> returnPlanVersionList = returnPlanVersionWrapper.selectList(projectApprovalOverallVersionIdList, IsDeleteEnum.NO.getKey());
        //查询回款计划详情列表
        List<ReturnPlanVersionDetail> returnPlanVersionDetailList = returnPlanVersionDetailWrapper.selectByVersionIdList(projectApprovalOverallVersionIdList, IsDeleteEnum.NO.getKey());

        ReturnApprovalBO bo = new ReturnApprovalBO();
        bo.setProjectId(projectId);
        bo.setRegionOverallVersionMap(regionOverallVersionMap);
        bo.setRegionOverallVersionIdList(regionOverallVersionIdList);
        bo.setProjectOverallVersionIdList(projectOverallVersionIdList);
        bo.setProjectApprovalOverallVersionIdList(projectApprovalOverallVersionIdList);
        bo.setProjectApprovalOverallVersionList(returnPlanVersionList);
        bo.setProjectApprovalOverallVersionDeatilList(returnPlanVersionDetailList);
        return bo;
    }

    /**
     * 获取区域全盘版本Map
     * 1.分期无战规后版本，取投资版、预算版中最新版
     * 2.分期一房一价前，取战规版、预算版、月度动态版中最新版
     * 3.分期一房一价后，取预算版、月度动态版中最新版
     * @param regionId 区域id
     * @return
     */
    private Map<String, Map<String, String>> getRegionOverallVersionMap(String regionId) {
        List<String> successVersionStates = Arrays.asList(VersionStatusEnum.SUCCESSAPPROVED.getKey());
        List<String> approvedVersionStates = Arrays.asList(VersionStatusEnum.SUCCESSAPPROVED.getKey(),VersionStatusEnum.APPROVED.getKey());
        List<String> beforeWarplanTypes = Arrays.asList(VersionTypeEunm.INVESTMENT.getKey(),VersionTypeEunm.BUDGET.getKey());
        List<String> beforeSaleTypes = Arrays.asList(VersionTypeEunm.WARPLAN.getKey(),VersionTypeEunm.BUDGET.getKey(),VersionTypeEunm.DYNAMIC.getKey());
        List<String> afterSaleTypes = Arrays.asList(VersionTypeEunm.BUDGET.getKey(),VersionTypeEunm.DYNAMIC.getKey());

        //查询区域下的回款计划版本
        List<ReturnPlanVersion> returnPlanVersionList = returnPlanVersionMapper.selectByRegionId(regionId, IsDeleteEnum.NO.getKey());
        //按照项目、分期分组(key:项目id,value:(key:分期id,value:回款计划列表))
        Map<String, Map<String, List<ReturnPlanVersion>>> versionListByProjectIdMap = returnPlanVersionList.stream()
                .collect(Collectors.groupingBy(ReturnPlanVersion::getProjectId,
                        Collectors.groupingBy(ReturnPlanVersion::getStageId)));

        //获取区域全盘版本Map(key:项目id,value:(key:分期id,value:版本id))
        Map<String, Map<String, String>> regionOverallVersionListMap = new HashMap<>();
        for(Map.Entry<String, Map<String, List<ReturnPlanVersion>>> projectVersionEntry :versionListByProjectIdMap.entrySet()){
            Map<String, String> projectVersionMap = new HashMap<>();

            String projectId = projectVersionEntry.getKey();
            Map<String, List<ReturnPlanVersion>> versionListByStageIdMap = projectVersionEntry.getValue();

            for(Map.Entry<String, List<ReturnPlanVersion>> stageVersionEntry :versionListByStageIdMap.entrySet()){
                String stageId = stageVersionEntry.getKey();
                List<ReturnPlanVersion> versionList = stageVersionEntry.getValue();
                //查询分期下审批通过一房一价的最新版本
                ReturnPlanVersion version = versionList.stream()
                        .filter(s-> successVersionStates.contains(s.getVersionState()) && s.getIsContainSale() == WhetherEnum.YES.getKey())
                        .max(Comparator.comparing(ReturnPlanVersion::getCreateTime)).orElse(null);

                //是否一房一价
                if(null == version){
                    //分期一房一价前，取战规版、预算版、月度动态版中最新版
                    version = versionList.stream()
                            .filter(s-> approvedVersionStates.contains(s.getVersionState()) && beforeSaleTypes.contains(s.getVersionType()))
                            .max(Comparator.comparing(ReturnPlanVersion::getCreateTime)).orElse(null);
                    //若无战规后版本，取投资版、预算版中最新版签约计划
                    if (null == version) {
                        version = versionList.stream()
                                .filter(s-> approvedVersionStates.contains(s.getVersionState()) && beforeWarplanTypes.contains(s.getVersionType()))
                                .max(Comparator.comparing(ReturnPlanVersion::getCreateTime)).orElse(null);
                    }
                }else {
                    //分期一房一价后，取预算版、月度动态版中最新版
                    version = versionList.stream()
                            .filter(s-> approvedVersionStates.contains(s.getVersionState()) && afterSaleTypes.contains(s.getVersionType()))
                            .max(Comparator.comparing(ReturnPlanVersion::getCreateTime)).orElse(null);
                }

                if(null != version){
                    projectVersionMap.put(stageId,version.getId());
                }
            }
            if(CollectionUtils.isNotEmpty(projectVersionMap)){
                regionOverallVersionListMap.put(projectId,projectVersionMap);
            }
        }
        return regionOverallVersionListMap;
    }

    /**
     * 获取待提交版本列表
     * @param projectId
     * @return
     */
    private List<ReturnPlanVersion> getToSubmitVersionList(String projectId) {
        List<ReturnPlanVersion> returnPlanVersionList = returnPlanVersionMapper.selectStageIdListByProjectIdGroupByStageIdOrderByCreateTimeDesc(projectId, IsDeleteEnum.NO.getKey());
        if(CollectionUtils.isEmpty(returnPlanVersionList)){
            throw new MsgException(CodeEnum.NOT_RETURN_PLAN_VERSION_ERROR);
        }

        //筛选待编制的回款计划版本列表
        List<ReturnPlanVersion> versions = returnPlanVersionList.stream()
                .filter(s -> VersionStatusEnum.NOTPREPARED.getKey().equals(s.getVersionState()))
                .collect(Collectors.toList());
        if(CollectionUtils.isNotEmpty(versions)){
            throw new MsgException(CodeEnum.RETURN_APPROVED_EXIST_NOT_PREPARED_VERSION_ERROR);
        }
        //未提交状态列表
        List<String> notSubmitStatusList = Arrays.asList(VersionStatusEnum.ORGANIZATION.getKey(), VersionStatusEnum.EXAMINEDAPPROVED.getKey(),VersionStatusEnum.FAILUREAPPROVED.getKey());
        //筛选待提交的回款计划版本列表
        List<ReturnPlanVersion> toSubmitVersionList = returnPlanVersionList.stream().filter(s -> notSubmitStatusList.contains(s.getVersionState())).collect(Collectors.toList());
        if(CollectionUtils.isEmpty(toSubmitVersionList)){
            throw new MsgException(CodeEnum.RETURN_APPROVED_NOT_SUBMIT_VERSION_IS_NULL_ERROR);
        }
        return toSubmitVersionList;
    }

    /**
     * 获取项目全盘年度回款指标列表
     * @param returnApprovalBO 回款审批相关数据
     * @return
     */
    private ReturnPlanProjectOverallYearTargetVo getProjectOverallYearTargetVo(ReturnApprovalBO returnApprovalBO,int year) {
        List<ReturnPlanVersion> versionList = returnApprovalBO.getProjectApprovalOverallVersionList();
        List<ReturnPlanVersionDetail> oldReturnPlanVersionDetailList = returnPlanVersionDetailWrapper.selectByVersionIdList(returnApprovalBO.getProjectOverallVersionIdList(), IsDeleteEnum.NO.getKey());

        List<ReturnPlanVersionDetail> versionDeatilList = returnApprovalBO.getProjectApprovalOverallVersionDeatilList();
        Map<String, List<ReturnPlanVersionDetail>> versionDeatilListMap = versionDeatilList.stream()
                .filter(s-> year == s.getReturnTime().getYear())
                .sorted(Comparator.comparing(ReturnPlanVersionDetail::getReturnTime))
                .collect(Collectors.groupingBy(ReturnPlanVersionDetail::getVersionId));

        //年度回款合计
        BigDecimal yearReturnTotal = BigDecimal.ZERO;
        //年度实际回款合计
        BigDecimal yearActualReturnTotal = BigDecimal.ZERO;
        //年度预测回款合计
        BigDecimal yearCalculateReturnTotal = BigDecimal.ZERO;
        //已签未回
        BigDecimal signNotReturn = BigDecimal.ZERO;
        //年度签约
        BigDecimal yearSign = BigDecimal.ZERO;
        //年初应收
        BigDecimal yearEarlyReceivable = BigDecimal.ZERO;
        //年末应收
        BigDecimal yearEndReceivable = BigDecimal.ZERO;

        for(ReturnPlanVersion version : versionList){
            //所属时间
            LocalDateTime affiliationTime = LocalDateTimeUtils.toLocalDateTime(version.getAffiliationMonth());
            List<ReturnPlanVersionDetail> versionDetails = versionDeatilListMap.get(version.getId());
            for(ReturnPlanVersionDetail detail : versionDetails){
                if(detail.getReturnTime().getMonthValue() == 1){
                    yearEarlyReceivable = yearEarlyReceivable.add(detail.getEarlyReceivableAmount());
                }
                if(detail.getReturnTime().getMonthValue() == 12){
                    yearEndReceivable = yearEndReceivable.add(detail.getFinalReceivable());
                }

                yearReturnTotal = yearReturnTotal.add(detail.getTotalReturnAmount());

                if(LocalDateTimeUtils.compareDate(detail.getReturnTime(),affiliationTime,0)){
                    yearActualReturnTotal = yearActualReturnTotal.add(detail.getIncrementReturnAmountActual().add(detail.getStoreReturnAmountActual()));
                }
                yearCalculateReturnTotal = yearCalculateReturnTotal
                        .add(detail.getIncrementReturnAmountCalculate().add(detail.getStoreReturnAmountCalculate()))
                        .add(detail.getIncrementReturnAmountAdjust().add(detail.getStoreReturnAmountAdjust()));
                yearSign = yearSign.add(detail.getSignAmount());
            }
        }

        ReturnPlanProjectOverallYearTargetVo vo = new ReturnPlanProjectOverallYearTargetVo();
        vo.setYear(year);
        vo.setYearReturnTotal(yearReturnTotal);
        vo.setYearActualReturnTotal(yearActualReturnTotal);
        vo.setYearCalculateReturnTotal(yearCalculateReturnTotal);
        vo.setYearSign(yearSign);
        vo.setYearEarlyReceivable(yearEarlyReceivable);
        vo.setYearEndReceivable(yearEndReceivable);
        vo.setSignNotReturn(vo.getYearEarlyReceivable());
        //上一版回款合计
        BigDecimal oldTotalReturnAmount = oldReturnPlanVersionDetailList.stream()
                .filter(s-> year == s.getReturnTime().getYear())
                .map(ReturnPlanVersionDetail::getTotalReturnAmount)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        //Vs上一版差异
        BigDecimal vsPreviousEditionDifference = yearReturnTotal.subtract(oldTotalReturnAmount);
        vo.setVsPreviousEditionDifference(vsPreviousEditionDifference);
        return vo;
    }

    /**
     * 获取区域年度回款目标
     * @param returnApprovalBO 回款审批相关数据
     * @return
     */
    private List<ReturnPlanRegionYearTargetVo> getRegionYearTargetVoList(ReturnApprovalBO returnApprovalBO) {
        String projectId = returnApprovalBO.getProjectId();
        List<String> regionOverallVersionIdList = returnApprovalBO.getRegionOverallVersionIdList();

        //查询回款计划版本列表
        List<ReturnPlanVersion> returnPlanVersionList = returnPlanVersionWrapper.selectList(regionOverallVersionIdList, IsDeleteEnum.NO.getKey());
        //筛选非本项目中审批通过的版本id列表
        List<String> successVersionIdList = returnPlanVersionList.stream()
                .filter(s -> !projectId.equals(s.getProjectId()) && VersionStatusEnum.SUCCESSAPPROVED.getKey().equals(s.getVersionState()))
                .map(s -> s.getId())
                .collect(Collectors.toList());
        //筛选非本项目中审批中的版本id列表
        List<String> approvedVersionIdList = returnPlanVersionList.stream()
                .filter(s -> !projectId.equals(s.getProjectId()) && VersionStatusEnum.APPROVED.getKey().equals(s.getVersionState()))
                .map(s -> s.getId())
                .collect(Collectors.toList());

        //查询回款计划版本详情列表
        List<ReturnPlanVersionDetail> detailList = returnPlanVersionDetailWrapper.selectByVersionIdList(regionOverallVersionIdList, IsDeleteEnum.NO.getKey());

        //审批通过回款合计Map(key:年份,value:回款合计)
        Map<Integer, BigDecimal> successMap = detailList.stream()
                .filter(s -> successVersionIdList.contains(s.getVersionId()))
                .collect(Collectors.toMap(s -> s.getReturnTime().getYear(), ReturnPlanVersionDetail::getTotalReturnAmount, BigDecimal::add));
        //审批中回款合计Map(key:年份,value:回款合计)
        Map<Integer, BigDecimal> approvedMap = detailList.stream()
                .filter(s -> approvedVersionIdList.contains(s.getVersionId()))
                .collect(Collectors.toMap(s -> s.getReturnTime().getYear(), ReturnPlanVersionDetail::getTotalReturnAmount, BigDecimal::add));
        //全部回款合计Map(key:年份,value:回款合计)
        Map<Integer, BigDecimal> totalMap = detailList.stream()
                .collect(Collectors.toMap(s -> s.getReturnTime().getYear(), ReturnPlanVersionDetail::getTotalReturnAmount,BigDecimal::add));


        List<ReturnPlanRegionYearTargetVo> voList = new ArrayList<>();
        //两年列表
        LocalDateTime now = LocalDateTime.now();
        List<Integer> yearList = Arrays.asList(now.getYear(), now.plusYears(1).getYear(),now.plusYears(2).getYear());
        for(int year : yearList){
            ReturnPlanRegionYearTargetVo vo = new ReturnPlanRegionYearTargetVo();
            vo.setYear(year);
            vo.setSuccessApproved(successMap.get(year) == null ? BigDecimal.ZERO : successMap.get(year));
            vo.setApproved(approvedMap.get(year) == null ? BigDecimal.ZERO : approvedMap.get(year));
            vo.setTotal(totalMap.get(year) == null ? BigDecimal.ZERO : totalMap.get(year));
            voList.add(vo);
        }
        return voList;
    }

    /**
     * 获取编辑版本列表
     * @param versionList
     * @return
     */
    private List<String> getEditedVersionList(List<ReturnPlanVersion> versionList) {
        return versionList.stream()
                .map(s -> s.getStageName() + ":" + VersionNameUtils.getVersionName(s))
                .collect(Collectors.toList());
    }

    /**
     * 审批提交
     * @param param 审批提交参数
     * @param user 用户
     * @return
     */
    @Transactional
    @Override
    public String approvalSubmit(ReturnPlanApprovalSubmitParam param, IdmUser user) {
        String projectId = param.getProjectId();
        String approvalId = param.getApprovalId();

        //参数校验
        if(StringUtils.isBlank(param.getProjectId()) && StringUtils.isBlank(approvalId)) {
            throw new MsgException(CodeEnum.REQUEST_PARAM_NOT_FULL_ERROR);
        }

        //查询是否存在未完成的审批单
        ApprovalBill approvalBill = new ApprovalBill();
        if(StringUtils.isBlank(approvalId)){
            approvalBill = approvalBillService.getNotSubmitApprovalBill(projectId, ProcessTypeEnum.MONEY_BACK_PLAN.getKey());
        }else {
            approvalBill = approvalBillService.getById(approvalId);
            projectId = approvalBill.getProjectId();
        }

        //获取审批流程
        String flowKey = submitApprovedProjectService.getFlowKey(ProcessTypeEnum.MONEY_BACK_PLAN.getKey(), projectId);
        if(StringUtils.isBlank(flowKey)){
            throw new MsgException(CodeEnum.FLOW_KEY_IS_NULL);
        }

        //已存在未完成的审批单 -> 更新审批内容，重新提交审批
        if(null != approvalBill){
            // 修改上传文件信息
            if (CollectionUtils.isNotEmpty(param.getFiles())) {
                fileUploadService.updateBillId(approvalBill.getId(), param.getFiles());
            }
            //获取审批内容
            ReturnPlanApprovalPreviewVo vo = this.getReturnPlanApprovalPreviewVo(projectId);

            ApprovalSubmitBO bo = new ApprovalSubmitBO();
            bo.setOaApprovalId(approvalBill.getId());
            bo.setAlias(user.getAlias());
            bo.setUsercn(user.getUsercn());
            bo.setFlowKey(flowKey);
            bo.setDescription(param.getDescription());
            bo.setSubject(approvalBill.getSubject());
            bo.setProcessType(approvalBill.getBusinessType());
            bo.setVars(vo.getVars());
            //将审批提交到OA
            approvalBill = approvalBillService.submit(bo);

            // 查询上传附件
            List<FilePreviewVO> files = fileUploadService.getFilePreview(approvalBill.getId());
            vo.setDescription(param.getDescription());
            vo.setFiles(files);
            //保存审批内容
            approvalPreviewService.saveData(approvalBill.getId(), JSON.toJSONString(vo),user);
        }else {
        //不存在未完成的审批单 -> 发起新的审批单,保存审批内容
            List<String> versionIdList = param.getVersionIdList();
            if(CollectionUtils.isEmpty(versionIdList)){
                throw new MsgException(CodeEnum.NOT_RETURN_PLAN_VERSION_ERROR);
            }
            List<ReturnPlanVersion> returnPlanVersionList = returnPlanVersionMapper.selectByIdList(versionIdList, IsDeleteEnum.NO.getKey());
            if(CollectionUtils.isEmpty(returnPlanVersionList)){
                throw new MsgException(CodeEnum.NOT_RETURN_PLAN_VERSION_ERROR);
            }
            //获取审批内容
            ReturnPlanApprovalPreviewVo vo = this.getApprovalPreview(projectId, approvalId);

            ApprovalSubmitBO bo = new ApprovalSubmitBO();
            ProjectByStagesDto project = projectByStagesMapper.selectByOrgdepid(projectId);
            bo.setAlias(user.getAlias());
            bo.setUsercn(user.getUsercn());
            bo.setFlowKey(flowKey);
            bo.setSubject("【回款计划】" + project.getOrgname() + "全周期回款计划审批");
            bo.setProcessType(ProcessTypeEnum.MONEY_BACK_PLAN.getKey());
            bo.setProjectId(projectId);
            List<ApprovalBillDetail> detailList = new ArrayList<>();;
            for(ReturnPlanVersion returnPlanVersion : returnPlanVersionList){
                ApprovalBillDetail approvalBillDetail = new ApprovalBillDetail();
                approvalBillDetail.setStageName(returnPlanVersion.getStageName());
                approvalBillDetail.setVersionId(returnPlanVersion.getId());
                approvalBillDetail.setVersionType(returnPlanVersion.getVersionType());
                approvalBillDetail.setVersionNum(returnPlanVersion.getVersionNum());
                detailList.add(approvalBillDetail);
            }
            bo.setDescription(param.getDescription());
            bo.setDetailList(detailList);
            bo.setVars(vo.getVars());

            //将审批提交到OA
            approvalBill = approvalBillService.submit(bo);
            // 修改上传文件信息
            if (CollectionUtils.isNotEmpty(param.getFiles())) {
                fileUploadService.updateBillId(approvalBill.getId(), param.getFiles());
            }
            // 查询上传附件
            List<FilePreviewVO> files = fileUploadService.getFilePreview(approvalBill.getId());
            String instanceId = approvalBill.getInstanceId();
            String taskId = approvalBill.getTaskId();
            String revoewUrl =approvalBillService.getReviewUrl(instanceId,taskId);
            vo.setApprovalId(approvalBill.getId());
            vo.setInstanceId(instanceId);
            vo.setTaskId(taskId);
            vo.setState(approvalBill.getState());
            vo.setReviewUrl(revoewUrl);
            vo.setFiles(files);
            vo.setDescription(approvalBill.getDescription());

            //保存审批内容
            approvalPreviewService.saveData(approvalBill.getId(), JSON.toJSONString(vo),user);

            //判断变更状态
            String versionState = returnPlanVersionList.get(0).getVersionState();
            if(!versionState.equals(VersionStatusEnum.FAILUREAPPROVED.getKey())){
                versionState = VersionStatusEnum.EXAMINEDAPPROVED.getKey();
            }
            //更新版本状态
            updateState(versionIdList,VersionEventEnum.INITIATE_APPROVAL.getKey(),VersionStatusEnum.EXAMINEDAPPROVED.getKey(),user);
        }
        //返回审批url
        return approvalBillService.getPreviewUrl(approvalBill);
    }

    /**
     * 查看全盘回款计划年份列表
     * @param projectId 项目id
     * @return
     */
    @Override
    public List<String> overallYearList(String projectId) {
        if(StringUtils.isBlank(projectId)){
            throw new MsgException(CodeEnum.REQUEST_PARAM_NOT_FULL_ERROR);
        }
        //获取全盘版本id列表
        List<String> versionIdList = getOverallVersionIdList(projectId);
        if(CollectionUtils.isEmpty(versionIdList)){
            return new ArrayList<>();
        }
        //获取年份列表
        return returnPlanVersionDetailMapper.selectYearListByVersionIdList(versionIdList,IsDeleteEnum.NO.getKey());
    }

    /**
     * 查看全盘回款计划一年数据
     * @param projectId 项目id
     * @param year 年份
     * @return
     */
    @Override
    public List<ReturnPlanYearDataVo> overallAnnualData(String projectId, String year) {
        if(StringUtils.isBlank(projectId) || StringUtils.isBlank(year)){
            throw new MsgException(CodeEnum.REQUEST_PARAM_NOT_FULL_ERROR);
        }
        //获取全盘版本id列表
        List<String> versionIdList = getOverallVersionIdList(projectId);
        List<ReturnPlanVersion> returnPlanVersionList = returnPlanVersionWrapper.selectList(versionIdList, IsDeleteEnum.NO.getKey());

        List<ReturnPlanYearDataVo> voList = new ArrayList<>();
        for(ReturnPlanVersion version :returnPlanVersionList){
            List<ReturnPlanYearDataVo> stageAnnualData = getAnnualDataByVersion(version, year);
            stageAnnualData.forEach(s->{
                if(OrgTypeEunm.ORG_STAGE.getKey().equals(s.getType())){
                    s.setName(s.getName() + "(" + VersionNameUtils.getVersionName(version)+")");
                    s.setPid(projectId);
                }
            });
            voList.addAll(stageAnnualData);
        }
        List<ReturnPlanYearDataVo> projectVoList = voList.stream()
                .filter(s -> OrgTypeEunm.ORG_STAGE.getKey().equals(s.getType()))
                .collect(Collectors.toList());

        ReturnPlanYearDataVo vo = new ReturnPlanYearDataVo();
        vo.setId(projectId);
        vo.setPid("0");
        vo.setType(OrgTypeEunm.ORG_PRODUCTF.getKey());
        vo.setName("全部");
        //汇总计算签约与回款
        SignAndReturnVo signAndReturn = summarySignAndReturn(projectVoList);
        //汇总年度数据
        ReturnPlanDetailYearData yearData = summaryYearData(projectVoList);
        //汇总计算月度数据列表
        List<ReturnPlanDetailMonthData> monthDataList = summaryMonthDataList(projectVoList);

        vo.setSignAndReturn(signAndReturn);
        vo.setYearData(yearData);
        vo.setMonthDataList(monthDataList);
        voList.add(vo);

        // 类型排序
        return typeOrderBy(voList);
    }

    /**
     * 审批校验
     * @param projectId 项目id
     */
    @Override
    public void approvedCheck(String projectId) {
        if(StringUtils.isBlank(projectId)){
            throw new MsgException(CodeEnum.REQUEST_PARAM_NOT_FULL_ERROR);
        }
        //获取待提交版本列表
        List<ReturnPlanVersion> toSubmitVersionList = getToSubmitVersionList(projectId);
        //获取待提交版本id列表
        List<String> toSubmitVersionIdList = toSubmitVersionList.stream().map(ReturnPlanVersion::getId).collect(Collectors.toList());
        //查询回款计划详情列表
        List<ReturnPlanVersionDetail> returnPlanVersionDetailList = returnPlanVersionDetailWrapper.selectByVersionIdList(toSubmitVersionIdList, IsDeleteEnum.NO.getKey());
        //筛选未编制完成的回款计划详情列表
        List<ReturnPlanVersionDetail> returnPlanVersionDetails = returnPlanVersionDetailList.stream().filter(s -> WhetherEnum.YES.getKey() != s.getIsEstablishment()).collect(Collectors.toList());
        if(CollectionUtils.isNotEmpty(returnPlanVersionDetails)){
            // 存在还没有编制完成的版本
            throw new MsgException(CodeEnum.ESTABLISHMENTNUM_ERROR);
        }
    }

    /**
     * 设置考核版
     * @param versionId 版本id
     */
    @Override
    public void setAssessment(String versionId) {
        if(StringUtils.isBlank(versionId)){
            throw new MsgException(CodeEnum.REQUEST_PARAM_NOT_FULL_ERROR);
        }
        //查询版本
        ReturnPlanVersion returnPlanVersion = returnPlanVersionMapper.selectById(versionId);
        if(null == returnPlanVersion || returnPlanVersion.getIsDelete() == IsDeleteEnum.YES.getKey()){
            throw new MsgException(CodeEnum.NOT_RETURN_PLAN_VERSION_ERROR);
        }
        if(!returnPlanVersion.getVersionState().equals(VersionStatusEnum.SUCCESSAPPROVED.getKey())){
            throw new MsgException(CodeEnum.RETURN_ASSESSMENTVERSION_NOT_SUCCESSAPPROVED_ERROR);
        }
        //查询来源签约版本
        String sourceSignId = returnPlanVersion.getSignPlanVersionId();
        SignPlanVersion signPlanVersion = signPlanVersionMapper.selectByIdAndDel(sourceSignId, IsDeleteEnum.NO.getKey());
        if(signPlanVersion.getIsAssessmentVersion() == null || signPlanVersion.getIsAssessmentVersion() !=WhetherEnum.YES.getKey()){
            throw new MsgException(CodeEnum.RETURN_ASSESSMENTVERSION_SIGN_NOT_ASSESSMENTVERSION_ERROR);
        }

        //清除考核版
        returnPlanVersionMapper.updateIsAssessmentByStageId(returnPlanVersion.getStageId(),WhetherEnum.NO.getKey(),IsDeleteEnum.NO.getKey());
        //设置考核版
        returnPlanVersion.setIsAssessment(WhetherEnum.YES.getKey());
        returnPlanVersionMapper.updateById(returnPlanVersion);
    }

    /**
     * 获取全盘版本列表
     * @param yearMonth
     */
    @Override
    public List<ReturnPlanVersion> getOverallVersionList(String yearMonth) {
        List<String> successVersionStates = Arrays.asList(VersionStatusEnum.SUCCESSAPPROVED.getKey());
        List<String> afterWarplanTypes = Arrays.asList(VersionTypeEunm.WARPLAN.getKey(),VersionTypeEunm.DYNAMIC.getKey());
        List<String> beforeWarplanTypes = Arrays.asList(VersionTypeEunm.INVESTMENT.getKey(),VersionTypeEunm.BUDGET.getKey());
        List<String> beforeSaleTypes = Arrays.asList(VersionTypeEunm.WARPLAN.getKey(),VersionTypeEunm.BUDGET.getKey(),VersionTypeEunm.DYNAMIC.getKey());
        List<String> afterSaleTypes = Arrays.asList(VersionTypeEunm.BUDGET.getKey(),VersionTypeEunm.DYNAMIC.getKey());

        //查询项目下的回款计划版本
        QueryWrapper<ReturnPlanVersion> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("is_delete",IsDeleteEnum.NO.getKey()).le("affiliation_month",yearMonth);
        List<ReturnPlanVersion> returnPlanVersionList = returnPlanVersionMapper.selectList(queryWrapper);
        //按照分期分组(key:项目id,value:(key:分期id,value:回款计划列表))
        Map<String,Map<String, List<ReturnPlanVersion>>> versionListByProjectIdIdMap = returnPlanVersionList.stream()
                .collect(Collectors.groupingBy(ReturnPlanVersion::getProjectId,Collectors.groupingBy(ReturnPlanVersion::getStageId)));

        List<ReturnPlanVersion> allVersionList = new ArrayList<>();
        for(Map.Entry<String, Map<String, List<ReturnPlanVersion>>> projectVersionEntry : versionListByProjectIdIdMap.entrySet()){

            List<ReturnPlanVersion> versionList = new ArrayList<>();
            for(Map.Entry<String, List<ReturnPlanVersion>> stageVersionEntry :projectVersionEntry.getValue().entrySet()){
                List<ReturnPlanVersion> versions = stageVersionEntry.getValue();
                //查询分期下审批通过一房一价的最新版本
                ReturnPlanVersion version = versions.stream()
                        .filter(s-> successVersionStates.contains(s.getVersionState()) && s.getIsContainSale() == WhetherEnum.YES.getKey())
                        .max(Comparator.comparing(ReturnPlanVersion::getCreateTime)).orElse(null);

                //是否一房一价
                if(null == version){
                    //获取获取战规后版本
                    version = versions.stream()
                            .filter(s-> successVersionStates.contains(s.getVersionState()) && afterWarplanTypes.contains(s.getVersionType()))
                            .max(Comparator.comparing(ReturnPlanVersion::getCreateTime)).orElse(null);
                    if(null == version){
                        //战规前，取投资版、预算版中最新版回款计划
                        version = versions.stream()
                                .filter(s-> successVersionStates.contains(s.getVersionState()) && beforeWarplanTypes.contains(s.getVersionType()))
                                .max(Comparator.comparing(ReturnPlanVersion::getCreateTime)).orElse(null);
                    }else {
                        //战规后，取战规版、预算版、月度动态版中最新版
                        version = versions.stream()
                                .filter(s-> successVersionStates.contains(s.getVersionState()) && beforeSaleTypes.contains(s.getVersionType()))
                                .max(Comparator.comparing(ReturnPlanVersion::getCreateTime)).orElse(null);
                    }
                }else {
                    //分期一房一价后，取预算版、月度动态版中最新版
                    version = versions.stream()
                            .filter(s-> successVersionStates.contains(s.getVersionState()) && afterSaleTypes.contains(s.getVersionType()))
                            .max(Comparator.comparing(ReturnPlanVersion::getCreateTime)).orElse(null);
                    if(null == version){
                        //取最新战规版
                        version = versions.stream()
                                .filter(s-> successVersionStates.contains(s.getVersionState()) && VersionTypeEunm.WARPLAN.getKey().equals(s.getVersionType()))
                                .max(Comparator.comparing(ReturnPlanVersion::getCreateTime)).orElse(null);
                    }
                    if(null == version){
                        //取最新投资版
                        version = versions.stream()
                                .filter(s-> successVersionStates.contains(s.getVersionState()) && VersionTypeEunm.INVESTMENT.getKey().equals(s.getVersionType()))
                                .max(Comparator.comparing(ReturnPlanVersion::getCreateTime)).orElse(null);
                    }
                }

                if(null != version){
                    versionList.add(version);
                }
            }
            if(CollectionUtils.isNotEmpty(versionList)){
                allVersionList.addAll(versionList);
            }
        }
        return allVersionList;
    }

    /**
     * 获取全盘版本id列表
     * @param projectId 项目id
     * @return
     */
    @Override
    public List<String> getOverallVersionIdList(String projectId) {
        List<String> successVersionStates = Arrays.asList(VersionStatusEnum.SUCCESSAPPROVED.getKey());
        List<String> beforeWarplanTypes = Arrays.asList(VersionTypeEunm.INVESTMENT.getKey(),VersionTypeEunm.BUDGET.getKey());
        List<String> beforeSaleTypes = Arrays.asList(VersionTypeEunm.WARPLAN.getKey(),VersionTypeEunm.BUDGET.getKey(),VersionTypeEunm.DYNAMIC.getKey());
        List<String> afterSaleTypes = Arrays.asList(VersionTypeEunm.BUDGET.getKey(),VersionTypeEunm.DYNAMIC.getKey());

        //查询项目下的回款计划版本
        List<ReturnPlanVersion> returnPlanVersionList = returnPlanVersionMapper.selectByProjectId(projectId, IsDeleteEnum.NO.getKey());
        //按照分期分组(key:分期id,value:回款计划列表)
        Map<String, List<ReturnPlanVersion>> versionListByStageIdMap = returnPlanVersionList.stream()
                .collect(Collectors.groupingBy(ReturnPlanVersion::getStageId));

        List<String> versionIdList = new ArrayList<>();
        for(Map.Entry<String, List<ReturnPlanVersion>> stageVersionEntry :versionListByStageIdMap.entrySet()){
            List<ReturnPlanVersion> versions = stageVersionEntry.getValue();
            //查询分期下审批通过一房一价的最新版本
            ReturnPlanVersion version = versions.stream()
                    .filter(s-> successVersionStates.contains(s.getVersionState()) && s.getIsContainSale() == WhetherEnum.YES.getKey())
                    .max(Comparator.comparing(ReturnPlanVersion::getCreateTime)).orElse(null);

            //是否一房一价
            if(null == version){
                //分期一房一价前，取战规版、预算版、月度动态版中最新版
                version = versions.stream()
                        .filter(s-> successVersionStates.contains(s.getVersionState()) && beforeSaleTypes.contains(s.getVersionType()))
                        .max(Comparator.comparing(ReturnPlanVersion::getCreateTime)).orElse(null);
                //若无战规后版本，取投资版、预算版中最新版签约计划
                if (null == version) {
                    version = versions.stream()
                            .filter(s-> successVersionStates.contains(s.getVersionState()) && beforeWarplanTypes.contains(s.getVersionType()))
                            .max(Comparator.comparing(ReturnPlanVersion::getCreateTime)).orElse(null);
                }
            }else {
                //分期一房一价后，取预算版、月度动态版中最新版
                version = versions.stream()
                        .filter(s-> successVersionStates.contains(s.getVersionState()) && afterSaleTypes.contains(s.getVersionType()))
                        .max(Comparator.comparing(ReturnPlanVersion::getCreateTime)).orElse(null);
                if(null == version){
                    //取最新战规版
                    version = versions.stream()
                            .filter(s-> successVersionStates.contains(s.getVersionState()) && VersionTypeEunm.WARPLAN.getKey().equals(s.getVersionType()))
                            .max(Comparator.comparing(ReturnPlanVersion::getCreateTime)).orElse(null);
                }
                if(null == version){
                    //取最新投资版
                    version = versions.stream()
                            .filter(s-> successVersionStates.contains(s.getVersionState()) && VersionTypeEunm.INVESTMENT.getKey().equals(s.getVersionType()))
                            .max(Comparator.comparing(ReturnPlanVersion::getCreateTime)).orElse(null);
                }
            }

            if(null != version){
                versionIdList.add(version.getId());
            }
        }
        return versionIdList;
    }

    /**
     * 获取全盘版本列表
     * @param projectId 项目id
     * @return
     */
    @Override
    public List<ReturnPlanVersion> getProjectOverallVersionList(String projectId) {
        List<String> successVersionStates = Arrays.asList(VersionStatusEnum.SUCCESSAPPROVED.getKey());
        List<String> beforeWarplanTypes = Arrays.asList(VersionTypeEunm.INVESTMENT.getKey(),VersionTypeEunm.BUDGET.getKey());
        List<String> beforeSaleTypes = Arrays.asList(VersionTypeEunm.WARPLAN.getKey(),VersionTypeEunm.BUDGET.getKey(),VersionTypeEunm.DYNAMIC.getKey());
        List<String> afterSaleTypes = Arrays.asList(VersionTypeEunm.BUDGET.getKey(),VersionTypeEunm.DYNAMIC.getKey());

        //查询项目下的回款计划版本
        List<ReturnPlanVersion> returnPlanVersionList = returnPlanVersionMapper.selectByProjectId(projectId, IsDeleteEnum.NO.getKey());
        //按照分期分组(key:分期id,value:回款计划列表)
        Map<String, List<ReturnPlanVersion>> versionListByStageIdMap = returnPlanVersionList.stream()
                .collect(Collectors.groupingBy(ReturnPlanVersion::getStageId));

        List<ReturnPlanVersion> versionList = new ArrayList<>();
        for(Map.Entry<String, List<ReturnPlanVersion>> stageVersionEntry :versionListByStageIdMap.entrySet()){
            List<ReturnPlanVersion> versions = stageVersionEntry.getValue();
            //查询分期下审批通过一房一价的最新版本
            ReturnPlanVersion version = versions.stream()
                    .filter(s-> successVersionStates.contains(s.getVersionState()) && s.getIsContainSale() == WhetherEnum.YES.getKey())
                    .max(Comparator.comparing(ReturnPlanVersion::getCreateTime)).orElse(null);

            //是否一房一价
            if(null == version){
                //分期一房一价前，取战规版、预算版、月度动态版中最新版
                version = versions.stream()
                        .filter(s-> successVersionStates.contains(s.getVersionState()) && beforeSaleTypes.contains(s.getVersionType()))
                        .max(Comparator.comparing(ReturnPlanVersion::getCreateTime)).orElse(null);
                //若无战规后版本，取投资版、预算版中最新版签约计划
                if (null == version) {
                    version = versions.stream()
                            .filter(s-> successVersionStates.contains(s.getVersionState()) && beforeWarplanTypes.contains(s.getVersionType()))
                            .max(Comparator.comparing(ReturnPlanVersion::getCreateTime)).orElse(null);
                }
            }else {
                //分期一房一价后，取预算版、月度动态版中最新版
                version = versions.stream()
                        .filter(s-> successVersionStates.contains(s.getVersionState()) && afterSaleTypes.contains(s.getVersionType()))
                        .max(Comparator.comparing(ReturnPlanVersion::getCreateTime)).orElse(null);
                if(null == version){
                    //取最新战规版
                    version = versions.stream()
                            .filter(s-> successVersionStates.contains(s.getVersionState()) && VersionTypeEunm.WARPLAN.getKey().equals(s.getVersionType()))
                            .max(Comparator.comparing(ReturnPlanVersion::getCreateTime)).orElse(null);
                }
                if(null == version){
                    //取最新投资版
                    version = versions.stream()
                            .filter(s-> successVersionStates.contains(s.getVersionState()) && VersionTypeEunm.INVESTMENT.getKey().equals(s.getVersionType()))
                            .max(Comparator.comparing(ReturnPlanVersion::getCreateTime)).orElse(null);
                }
            }

            if(null != version){
                versionList.add(version);
            }
        }
        return versionList;
    }

    /**
     * 更新项目下分期最新版本标识
     * @param projectId
     */
    private void updateStageNewestByProjectId(String projectId) {
        List<String> successVersionStates = Arrays.asList(VersionStatusEnum.SUCCESSAPPROVED.getKey(),VersionStatusEnum.SYSGENERATION.getKey());
        List<String> beforeWarplanTypes = Arrays.asList(VersionTypeEunm.INVESTMENT.getKey(),VersionTypeEunm.BUDGET.getKey());
        List<String> beforeSaleTypes = Arrays.asList(VersionTypeEunm.WARPLAN.getKey(),VersionTypeEunm.BUDGET.getKey(),VersionTypeEunm.DYNAMIC.getKey());
        List<String> afterSaleTypes = Arrays.asList(VersionTypeEunm.BUDGET.getKey(),VersionTypeEunm.DYNAMIC.getKey());

        //查询项目下的回款计划版本
        List<ReturnPlanVersion> returnPlanVersionList = returnPlanVersionMapper.selectByProjectId(projectId, IsDeleteEnum.NO.getKey());
        //按照分期分组(key:分期id,value:回款计划列表)
        Map<String, List<ReturnPlanVersion>> versionListByStageIdMap = returnPlanVersionList.stream()
                .collect(Collectors.groupingBy(ReturnPlanVersion::getStageId));

        List<String> versionIdList = new ArrayList<>();
        for(Map.Entry<String, List<ReturnPlanVersion>> stageVersionEntry :versionListByStageIdMap.entrySet()){
            List<ReturnPlanVersion> versions = stageVersionEntry.getValue();
            //查询分期下审批通过一房一价的最新版本
            ReturnPlanVersion version = versions.stream()
                    .filter(s-> successVersionStates.contains(s.getVersionState()) && s.getIsContainSale() == WhetherEnum.YES.getKey())
                    .max(Comparator.comparing(ReturnPlanVersion::getCreateTime)).orElse(null);

            //是否一房一价
            if(null == version){
                //分期一房一价前，取战规版、预算版、月度动态版中最新版
                version = versions.stream()
                        .filter(s-> successVersionStates.contains(s.getVersionState()) && beforeSaleTypes.contains(s.getVersionType()))
                        .max(Comparator.comparing(ReturnPlanVersion::getCreateTime)).orElse(null);
                //若无战规后版本，取投资版、预算版中最新版签约计划
                if (null == version) {
                    version = versions.stream()
                            .filter(s-> successVersionStates.contains(s.getVersionState()) && beforeWarplanTypes.contains(s.getVersionType()))
                            .max(Comparator.comparing(ReturnPlanVersion::getCreateTime)).orElse(null);
                }
            }else {
                //分期一房一价后，取预算版、月度动态版中最新版
                version = versions.stream()
                        .filter(s-> successVersionStates.contains(s.getVersionState()) && afterSaleTypes.contains(s.getVersionType()))
                        .max(Comparator.comparing(ReturnPlanVersion::getCreateTime)).orElse(null);
            }

            if(null != version){
                versionIdList.add(version.getId());
            }
        }

        returnPlanVersionList.forEach(s->{
            if(versionIdList.contains(s.getId())){
                s.setIsStageNewest(WhetherEnum.YES.getKey());
            }else {
                s.setIsStageNewest(WhetherEnum.NO.getKey());
            }
        });
        //更新isStageNewest标识
        updateBatchById(returnPlanVersionList);
    }


    /**
     * 是否允许自动生成回款计划版本
     * @param stageId 分期id
     * @param versionType 版本类型
     * @return
     */
    private boolean whetherAutomaticCreate(String stageId, String versionType) {
        List<String> versionTypeList = Arrays.asList(VersionTypeEunm.INVESTMENT.getKey(),VersionTypeEunm.WARPLAN.getKey(), VersionTypeEunm.DYNAMIC.getKey());
        if(versionTypeList.contains(versionType)){
            List<String> versionStatusList = Arrays.asList(VersionStatusEnum.NOTPREPARED.getKey(), VersionStatusEnum.ORGANIZATION.getKey(),
                    VersionStatusEnum.ORGANIZATION.getKey(), VersionStatusEnum.EXAMINEDAPPROVED.getKey(),
                    VersionStatusEnum.APPROVED.getKey(), VersionStatusEnum.FAILUREAPPROVED.getKey());
            ReturnPlanVersion version = returnPlanVersionWrapper.selectLatestByStageIdAndVersionTypeAndVersionState(
                    stageId, versionType,null, versionStatusList, IsDeleteEnum.NO.getKey());
            return null == version;
        }
        return false;
    }


    /**
     * 校验是否符合创建回款计划通用规则
     * @param stage 分期数据
     */
    private void whetherCreate(ProjectByStagesDto stage) {
        //项目下存在待提交审批、审批中、审批被驳回状态的回款计划版本时，不允许新建回款计划版本
        List<String> stateList = asList(VersionStatusEnum.EXAMINEDAPPROVED.getKey(), VersionStatusEnum.APPROVED.getKey(),VersionStatusEnum.FAILUREAPPROVED.getKey());
        //查询回款计划数量
        Integer count = returnPlanVersionWrapper.selectCountByProjectIdAndVersionState(stage.getParentOrgid(), stateList, IsDeleteEnum.NO.getKey());
        if (count > 0) {
            throw new MsgException(CodeEnum.CREATE_RETURN_PLAN_BY_PROJECT_ERROR);
        }

        //分期已存在待编制、编制中的回款计划版本时，不允许新建回款计划版本
        stateList = asList(VersionStatusEnum.NOTPREPARED.getKey(), VersionStatusEnum.ORGANIZATION.getKey());
        //查询回款计划数量
        count = returnPlanVersionWrapper.selectCountByStageIdAndVersionState(stage.getOrgdepid(), stateList, IsDeleteEnum.NO.getKey());
        if (count > 0) {
            throw new MsgException(CodeEnum.CREATE_RETURN_PLAN_BY_STAGE_ERROR);
        }
    }

    /**
     * 校验是否符合各版本类型对应的创建回款计划规则
     * @param stageId 分期id
     * @param versionType 版本类型
     */
    private void whetherCreateByType(String stageId, String versionType) {
        //查询最新审批通过的回款计划版本
        ReturnPlanVersion version = returnPlanVersionWrapper.selectLatestByStageIdAndVersionTypeAndVersionState(
                stageId, versionType,null, Arrays.asList(VersionStatusEnum.SUCCESSAPPROVED.getKey()), IsDeleteEnum.NO.getKey());
        if(null == version){
            return;
        }

        //投资版回款计划创建
        //1、分期战规后不允许普通用户新建该分期投资版回款计划版本
        //2、如果分期下没有投资版回款计划，不受规则“1”限制，可直接新建投资版回款计划
        if(VersionTypeEunm.INVESTMENT.getKey().equals(versionType)){
            if(signPlanVersionService.isAfterWarplan(stageId)){
                throw new MsgException(CodeEnum.CREATE_RETURN_PLAN_BY_AFTER_WARPLAN_ERROR);
            }
            //战规版回款计划创建
            // 1、分期大定价后不允许普通用户新建该分期战规版回款计划版本
            // 2、如果分期下没有战规版回款计划，不受规则“2”限制，可直接新建战规版回款计划
        }else if(VersionTypeEunm.WARPLAN.getKey().equals(versionType)){
            List<String> versionStateList = Arrays.asList(VersionStatusEnum.SUCCESSAPPROVED.getKey(), VersionStatusEnum.SYSGENERATION.getKey());
            Integer count = priceVersionWrapper.selectCountByProjectfIdAndVersionTypeAndVersionState(
                    stageId, VersionTypeEunm.GETPRICE.getKey(), versionStateList, IsDeleteEnum.NO.getKey());
            if (null != count && count > 0) {
                throw new MsgException(CodeEnum.EXIST_APPROVAL_WARPLAN_VERSIONS);
            }
        }
    }

    /**
     * 创建回款计划版本
     * @param stage 分期数据
     * @param versionType 版本类型
     * @param versionCreate 创建方式
     * @param user 用户
     */
    private ReturnPlanVersion createReturnPlanVersion(ProjectByStagesDto stage, String versionType,String versionIdentify, String versionCreate, IdmUser user) {
        SignPlanVersion signPlanVersion = signPlanVersionWrapper.selectLatestByStageIdAndVersionTypeAndVersionState(stage.getOrgdepid(), versionType, VersionStatusEnum.SUCCESSAPPROVED.getKey(), IsDeleteEnum.NO.getKey());
        if(null == signPlanVersion){
            throw new MsgException(CodeEnum.NOT_SIGN_PLAN_VERSION);
        }
        //分期的经营方式全部都是可租或不可租售的分期，不允许创建版本
    	if (!getStageIsSaly(signPlanVersion.getSourceSupplyId())) {
	          throw new MsgException(CodeEnum.CREATE_NOT_BUILD_SALY_TYPE_ERROR);
    	}
    	
        //签约计划不存在同版本标签预算版不允许复制
  		if(StrUtils.isNotEmpty(versionIdentify) && !versionIdentify.equals(signPlanVersion.getVersionIdentify())) {
  			throw new MsgException(CodeEnum.NOT_SIGN_BUDGET_VERSION);
  		}
        
        LocalDateTime now = LocalDateTime.now();
        //版本归属年月
        String affiliationMonth = LocalDateTimeUtils.formatTime(now, DateUtils.DATE_JFP_STR);
        //是否考核版
        int isAssessment = 0;
        if (versionType.equals(VersionTypeEunm.INVESTMENT.getKey())) {
            if(signPlanVersion.getIsAssessmentVersion() !=null && signPlanVersion.getIsAssessmentVersion() == WhetherEnum.YES.getKey()){
                //查询当前分期下是否存在投资考核版回款计划
                int count = returnPlanVersionWrapper.selectCountByIsAssessmentAndStageId(stage.getOrgdepid(),VersionTypeEunm.INVESTMENT.getKey(),WhetherEnum.YES.getKey(),IsDeleteEnum.NO.getKey());
                if(count == 0){
                    isAssessment = 1;
                }else {
                    isAssessment = 0;
                }
            }
        }else if(!signPlanVersion.getAffiliationMonth().equals(affiliationMonth)){
            throw new MsgException(CodeEnum.CREATE_RETURN_PLAN_NOT_CURRENT_MONTH_SIGN_PLAN_VERSION_ERROR);
        }

        //获取创建回款计划版本来源回款系数名称
        String returnCoefficientName = returnPlanVersionCoefficientService.getCreateSourceCoefficient(stage.getParentOrgid(),signPlanVersion.getId());

        ReturnPlanVersion returnPlanVersion = new ReturnPlanVersion();

        String id = UUIDUtils.create();
        //项目id
        String projectId = stage.getParentOrgid();
        //分期id
        String stageId = stage.getOrgdepid();
        //版本号
        String versionNum = getVersionNum(stage.getOrgdepid(), versionType,versionIdentify, affiliationMonth);
        //分期取数系统
        SysProjectFetchNumber sysProjectFetchNumber = sysProjectFetchNumberService.selectByFid(stageId);
        //版本状态
        String versionStatus;
        if(VersionCreateEnum.AUTOMATICCREATE.getKey().equals(versionCreate)){
            versionStatus = VersionStatusEnum.ORGANIZATION.getKey();
        }else {
            versionStatus = VersionStatusEnum.NOTPREPARED.getKey();
        }
        //是否进入一房一价
        int isContainSale = 0;
        List<MymRooms> rooms = mymRoomsService.selectNewestWarPlanByFid(stageId);
        if(CollectionUtils.isNotEmpty(rooms)){
            isContainSale =  1;
        }

        returnPlanVersion.setId(id);
        returnPlanVersion.setVersionType(versionType);
        returnPlanVersion.setVersionNum(versionNum);
        returnPlanVersion.setProjectId(projectId);
        returnPlanVersion.setStageId(stageId);
        returnPlanVersion.setVersionState(versionStatus);
        returnPlanVersion.setSignPlanVersionId(signPlanVersion.getId());
        returnPlanVersion.setSupplyPlanVersionId(signPlanVersion.getSourceSupplyId());
        returnPlanVersion.setResourceMatching(null);
        returnPlanVersion.setAnnualObjectives(null);
        returnPlanVersion.setCompileYear(null);
        returnPlanVersion.setVersionIdentify(versionIdentify);
        returnPlanVersion.setSourceVersionId(null);
        returnPlanVersion.setIsAssessment(isAssessment);
        returnPlanVersion.setAffiliationMonth(affiliationMonth);
        returnPlanVersion.setDataSource(sysProjectFetchNumber == null ? null : sysProjectFetchNumber.getCountingSystemId());
        returnPlanVersion.setIsFinalization(WhetherEnum.NO.getKey());
        returnPlanVersion.setCreateBy(user.getAlias());
        returnPlanVersion.setCreateTime(now);
        returnPlanVersion.setVersionCreate(versionCreate);
        returnPlanVersion.setUpdateBy(user.getAlias());
        returnPlanVersion.setUpdateTime(now);
        returnPlanVersion.setIsDelete(IsDeleteEnum.NO.getKey());
        returnPlanVersion.setDescription(null);
        returnPlanVersion.setIsStageNewest(WhetherEnum.NO.getKey());
        returnPlanVersion.setIsContainSale(isContainSale);
        returnPlanVersionMapper.insert(returnPlanVersion);

        if(VersionCreateEnum.AUTOMATICCREATE.getKey().equals(versionCreate)){
            saveVersionLog(Arrays.asList(id),VersionEventEnum.SYS_GENERATE.getKey(),"",user.getAlias(),"新建回款计划",null);
        }else {
            saveVersionLog(Arrays.asList(id),VersionEventEnum.MANUALCREATE.getKey(),"",user.getAlias(),"新建回款计划",null);

        }
        return returnPlanVersion;
    }

    /**
     * 获取版本号
     * @param stageId 分期id
     * @param versionType 版本类型
     * @param affiliationMonth
     * @return
     */
    @Override
    public String getVersionNum(String stageId, String versionType,String versionIdentify, String affiliationMonth) {
        // 查询最新审批通过的回款计划版本
        ReturnPlanVersion version = returnPlanVersionWrapper.selectLatestByStageIdAndVersionTypeAndVersionState(
                stageId, versionType,versionIdentify, Arrays.asList(VersionStatusEnum.SUCCESSAPPROVED.getKey(),VersionStatusEnum.SYSGENERATION.getKey()), IsDeleteEnum.NO.getKey());
        String versionNum = null;
        //版本号赋值处理
        if (null != version) {
            if (VersionTypeEunm.DYNAMIC.getKey().equals(versionType)) {
                if (version.getAffiliationMonth().equals(affiliationMonth)) {
                    versionNum = version.getVersionNum();
                }
            }else {
                versionNum = version.getVersionNum();
            }
        }
        return VersionUtils.bigVersionInc(versionNum);
    }

    /**
     * 获取回款计划版本数据
     */
    private ReturnPlanVersionDataVo getVersionFullNameVo(ReturnPlanVersion returnPlanVersion) {
        ReturnPlanVersionDataVo vo = new ReturnPlanVersionDataVo();
        //版本id
        String signPlanVersionId = returnPlanVersion.getId();
        // 版本号
        String versionNum = returnPlanVersion.getVersionNum();
        // 版本状态编码
        String versionStateCode = returnPlanVersion.getVersionState();
        // 版本状态名
        String versionState = VersionStatusEnum.getValueByKey(versionStateCode);
        // 版本类型
        String versionType = returnPlanVersion.getVersionType();
        // 版本类型名称
        String versionTypeName = VersionTypeEunm.getValueByKey(versionType);
        // 归属月份
        String signAffiliationMonth = returnPlanVersion.getAffiliationMonth();
        String affiliationMonth = null == signAffiliationMonth
                ? null : signAffiliationMonth.replaceAll("-", "");
        // 签约计划预算版版本属性
        String versionIdentify = returnPlanVersion.getVersionIdentify();
        // 是否考核版
        String isAssessmentVersion = String.valueOf(returnPlanVersion.getIsAssessment() == null
                ? 0 : returnPlanVersion.getIsAssessment());

        //版本创建人
        String createUser = null;
        //用户账号
        String alias = returnPlanVersion.getCreateBy();
        if(Constants.SYS.equals(alias)){
            createUser = "系统";
        }else {
            createUser = idmUserService.getByAlias(alias).getUsercn();
        }

        //版本创建时间
        LocalDateTime createTime = returnPlanVersion.getCreateTime();
        //版本修改时间
        LocalDateTime updateTime = returnPlanVersion.getUpdateTime();
        //版本创建方式编码
        String createMethodCode = returnPlanVersion.getVersionCreate();
        //版本创建方式名称
        String createMethodName = VersionCreateEnum.getKeyByValue(createMethodCode);
        
        if(versionType.equals(VersionTypeEunm.BUDGET.getKey())) {
        	isAssessmentVersion =  "0";
        	//来源版本id
            String sourceVersionId = returnPlanVersion.getSourceVersionId();
            //来源计划版本
            ReturnPlanVersion sourceReturnPlanVersion = returnPlanVersionMapper.selectById(sourceVersionId);
            //来源版本号
            String sourceVersionNum = sourceReturnPlanVersion.getVersionNum();
            //来源版本类型
            String sourceVersionTypeCode = sourceReturnPlanVersion.getVersionType();
            //来源版本类型名称
            String sourceVersionTypeName = VersionTypeEunm.getValueByKey(sourceVersionTypeCode);
            //来源版本预算版版本属性
            String sourceVersionIdentify = sourceReturnPlanVersion.getVersionIdentify();
            //来源版本创建时间
            LocalDateTime sourceCreateTime = sourceReturnPlanVersion.getCreateTime();
            //来源版本修改时间
            LocalDateTime sourceUpdateTime = sourceReturnPlanVersion.getUpdateTime();
            //来源版本归属月份
            String sourceAffiliationMonth = null == sourceReturnPlanVersion.getAffiliationMonth()
                    ? null : sourceReturnPlanVersion.getAffiliationMonth().replaceAll("-", "");

            String sourceIsAssessmentVersion = "0";
            if(sourceReturnPlanVersion.getVersionType().equals(VersionTypeEunm.INVESTMENT.getKey())){
                sourceIsAssessmentVersion = String.valueOf(sourceReturnPlanVersion.getIsAssessment() == null
                        ? 0 : sourceReturnPlanVersion.getIsAssessment().toString());
            }
            vo.setSourceVersionId(sourceVersionId);
            vo.setSourceVersionNum(sourceVersionNum);
            vo.setSourceVersionTypeCode(sourceVersionTypeCode);
            vo.setSourceVersionTypeName(sourceVersionTypeName);
            vo.setSourceAffiliationMonth(sourceAffiliationMonth);
            vo.setSourceVersionIdentify(sourceVersionIdentify);
            vo.setSourceCreateTime(sourceCreateTime);
            vo.setSourceUpdateTime(sourceUpdateTime);
            vo.setSourceIsAssessmentVersion(sourceIsAssessmentVersion);
        }else {
        	//来源版本id
            String sourceVersionId = returnPlanVersion.getSignPlanVersionId();
            //来源计划版本
            SignPlanVersion signPlanVersion = signPlanVersionMapper.selectById(sourceVersionId);
            //来源版本号
            String sourceVersionNum = signPlanVersion.getVersionNum();
            //来源版本类型
            String sourceVersionTypeCode = signPlanVersion.getVersionType();
            //来源版本类型名称
            String sourceVersionTypeName = VersionTypeEunm.getValueByKey(sourceVersionTypeCode);
            //来源版本预算版版本属性
            String sourceVersionIdentify = signPlanVersion.getVersionIdentify();
            //来源版本创建时间
            LocalDateTime sourceCreateTime = signPlanVersion.getCreateTime();
            //来源版本修改时间
            LocalDateTime sourceUpdateTime = signPlanVersion.getUpdateTime();
            //来源版本归属月份
            String sourceAffiliationMonth = null == signPlanVersion.getAffiliationMonth()
                    ? null : signPlanVersion.getAffiliationMonth().replaceAll("-", "");

            String sourceIsAssessmentVersion = "0";
            if(signPlanVersion.getVersionType().equals(VersionTypeEunm.INVESTMENT.getKey())){
                sourceIsAssessmentVersion = String.valueOf(signPlanVersion.getIsAssessmentVersion() == null
                        ? 0 : signPlanVersion.getIsAssessmentVersion().toString());
            }
            
            vo.setSourceVersionId(sourceVersionId);
            vo.setSourceVersionNum(sourceVersionNum);
            vo.setSourceVersionTypeCode(sourceVersionTypeCode);
            vo.setSourceVersionTypeName(sourceVersionTypeName);
            vo.setSourceAffiliationMonth(sourceAffiliationMonth);
            vo.setSourceVersionIdentify(sourceVersionIdentify);
            vo.setSourceCreateTime(sourceCreateTime);
            vo.setSourceUpdateTime(sourceUpdateTime);
            vo.setSourceIsAssessmentVersion(sourceIsAssessmentVersion);
        }
        

        vo.setVersionId(signPlanVersionId);
        vo.setVersionNum(versionNum);
        vo.setVersionState(versionState);
        vo.setVersionStateCode(versionStateCode);
        vo.setVersionType(versionType);
        vo.setVersionTypeName(versionTypeName);
        vo.setAffiliationMonth(affiliationMonth);
        vo.setVersionIdentify(versionIdentify);
        vo.setIsAssessmentVersion(isAssessmentVersion);
        vo.setCreateUser(createUser);
        vo.setCreateTime(createTime);
        vo.setUpdateTime(updateTime);
        vo.setCreateMethodCode(createMethodCode);
        vo.setCreateMethodName(createMethodName);
        return vo;
    }


    /**
     * 版本日志
     * @param versionIdList 版本id列表
     * @param event 版本事件
     * @param beforeState 变更前的状态
     * @param alias 创建人
     * @param title 事件标题
     * @param content 事件内容
     */
    private void saveVersionLog(List<String> versionIdList, String event, String beforeState, String alias, String title, String content){
        List<VersionLog> versionLogList = new ArrayList<>();
        List<ReturnPlanVersion> returnPlanVersionList = returnPlanVersionMapper.selectBatchIds(versionIdList);
        for(ReturnPlanVersion version : returnPlanVersionList){
            String versionName = VersionNameUtils.getVersionName(version);
            VersionLog versionLog = new VersionLog();
            versionLog.setId(UUIDUtils.create());
            versionLog.setProjectId(version.getProjectId());
            versionLog.setProjectfId(version.getStageId());
            versionLog.setModuleType(ModuleTypeEunm.RETUEN_PLAN.getKey());
            versionLog.setEvent(event);
            versionLog.setVersionId(version.getId());
            versionLog.setVersionName(versionName);
            versionLog.setBeforeState(beforeState);
            versionLog.setAfterState(version.getVersionState());
            versionLog.setModifySourceType(null);
            versionLog.setModifySourceId(null);
            versionLog.setOperationBy(alias);
            versionLog.setOperationTime(LocalDateTime.now());
            versionLog.setTitle(title);
            versionLog.setContent(content);
            versionLog.setIsDelete(0);
            versionLog.setCreateBy(alias);
            versionLog.setCreateTime(LocalDateTime.now());
            versionLogList.add(versionLog);
        }
        versionLogService.saveBatch(versionLogList);
    }
}
